From 945823e8a68470ed92398e9ee8dcc800af43f37c Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:22:13 +1000 Subject: [PATCH 01/43] Adjust `std::io::Error` tests to only assess public API Viewing internals wont be possible from `std` once moved into `core`. --- library/std/src/io/error/tests.rs | 45 ++++++++----------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a8eef06381dae..a3a2f5830ae91 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use crate::io::{Error, ErrorKind, const_error}; use crate::sys::io::{decode_error_kind, error_string}; use crate::{assert_matches, error, fmt}; @@ -12,12 +12,7 @@ fn test_debug_error() { let code = 6; let msg = error_string(code); let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; + let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); let expected = format!( "Custom {{ \ kind: InvalidInput, \ @@ -70,10 +65,6 @@ fn test_os_packing() { for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); } } @@ -82,28 +73,17 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); } #[test] fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; + use ErrorKind::*; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; // Check that the public api is right. assert_eq!(e.kind(), $kind); assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); }}; } @@ -128,14 +108,11 @@ impl fmt::Display for Bojji { #[test] fn test_custom_error_packing() { - use super::Custom; let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); + assert_eq!(test.kind(), ErrorKind::Uncategorized); assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), + test.get_ref(), + Some(error) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), ); } @@ -181,11 +158,11 @@ fn test_std_io_error_downcast() { assert_eq!(kind, io_error.kind()); // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); + const KIND: ErrorKind = ErrorKind::Other; + const MESSAGE: &str = "simple message error test"; + let io_error = const_error!(KIND, MESSAGE); let io_error = io_error.downcast::().unwrap_err(); - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); + assert_eq!(KIND, io_error.kind()); + assert_eq!(MESSAGE, format!("{io_error}")); } From 60bac0298e322161fbbe1362cbe2cf9033c44a3d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:26:02 +1000 Subject: [PATCH 02/43] Adjust `Error` documentation Adjust `Error` documentation `core` is more restrictive with documentation quality and linking to other items. Methods that will be implemented through incoherence must also be explicitly linked. --- library/std/src/io/error.rs | 53 ++++++++++++---------- library/std/src/io/error/repr_bitpacked.rs | 19 ++++---- library/std/src/os/unix/process.rs | 4 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 360ca83c65a91..67ffc79525b3a 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -35,11 +35,11 @@ use crate::{error, fmt, result, sys}; /// /// While usual Rust style is to import types directly, aliases of [`Result`] /// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias /// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`std::result::Result`][`Result`]. +/// of [`core::result::Result`][`Result`]. /// -/// [`std::io`]: crate::io +/// [`std::io`]: ../../std/io/index.html /// [`io::Error`]: Error /// [`Result`]: crate::result::Result /// [prelude]: crate::prelude @@ -63,16 +63,16 @@ use crate::{error, fmt, result, sys}; #[doc(search_unbox)] pub type Result = result::Result; -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and /// associated traits. /// /// Errors mostly originate from the underlying OS, but custom instances of /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Error { repr: Repr, @@ -154,7 +154,7 @@ enum ErrorData { // have on 32 bit platforms. // // (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't // matter at all) #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] @@ -167,9 +167,11 @@ pub struct SimpleMessage { /// Creates a new I/O error from a known kind of error and a string literal. /// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in /// `const` contexts. /// +/// [new]: ../../std/io/struct.Error.html#method.new +/// /// # Example /// ``` /// #![feature(io_const_error)] @@ -262,9 +264,11 @@ impl Error { /// Creates a new I/O error from an arbitrary error payload. /// /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`] + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] /// with [`ErrorKind::Other`]. /// + /// [new]: struct.Error.html#method.new + /// /// # Examples /// /// ``` @@ -367,12 +371,12 @@ impl Error { /// Returns the OS error that this error represents (if any). /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise /// it will return [`None`]. /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error /// /// # Examples /// @@ -408,10 +412,10 @@ impl Error { /// Returns a reference to the inner error wrapped by this error (if any). /// - /// If this [`Error`] was constructed via [`new`] then this function will + /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// - /// [`new`]: Error::new + /// [new]: ../../std/io/struct.Error.html#method.new /// /// # Examples /// @@ -448,10 +452,10 @@ impl Error { /// Returns a mutable reference to the inner error wrapped by this error /// (if any). /// - /// If this [`Error`] was constructed via [`new`] then this function will + /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// - /// [`new`]: Error::new + /// [new]: ../../std/io/struct.Error.html#method.new /// /// # Examples /// @@ -521,12 +525,12 @@ impl Error { /// Consumes the `Error`, returning its inner error (if any). /// - /// If this [`Error`] was constructed via [`new`] or [`other`], + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], /// then this function will return [`Some`], /// otherwise it will return [`None`]. /// - /// [`new`]: Error::new - /// [`other`]: Error::other + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other /// /// # Examples /// @@ -571,8 +575,9 @@ impl Error { /// /// This method is meant to be a convenience routine for calling /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`]. + /// [`Error::into_inner`][into_inner]. /// + /// [into_inner]: struct.Error.html#method.into_inner /// /// # Examples /// @@ -655,9 +660,9 @@ impl Error { /// This may be a value set by Rust code constructing custom `io::Error`s, /// or if this `io::Error` was sourced from the operating system, /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. + /// See [`last_os_error`][last_os_error] for more details. /// - /// [`last_os_error`]: Error::last_os_error + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// /// # Examples /// diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 7c237825459af..3b81759eb86e2 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -146,7 +146,8 @@ impl Repr { // (rather than `ptr::wrapping_add`), but it's unclear this would give // any benefit, so we just use `wrapping_add` instead. let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); - // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // SAFETY: + // `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, // because `p`'s alignment means it isn't allowed to have any of the // `TAG_BITS` set (you can verify that addition and bitwise-or are the // same when the operands have no bits in common using a truth table). @@ -166,7 +167,7 @@ impl Repr { #[inline] pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; - // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, @@ -183,7 +184,7 @@ impl Repr { #[inline] pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; - // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, @@ -200,26 +201,26 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - // Safety: References are never null. + // SAFETY: References are never null. Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) } #[inline] pub(super) fn data(&self) -> ErrorData<&Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &*c) } } #[inline] pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &mut *c) } } #[inline] pub(super) fn into_data(self) -> ErrorData> { let this = core::mem::ManuallyDrop::new(self); - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } } @@ -228,7 +229,7 @@ impl Repr { impl Drop for Repr { #[inline] fn drop(&mut self) { - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { let _ = decode_repr(self.0, |p| Box::::from_raw(p)); @@ -255,7 +256,7 @@ where let kind_bits = (bits >> 32) as u32; let kind = ErrorKind::from_prim(kind_bits).unwrap_or_else(|| { debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); - // This means the `ptr` passed in was not valid, which violates + // SAFETY: This means the `ptr` passed in was not valid, which violates // the unsafe contract of `decode_repr`. // // Using this rather than unwrap meaningfully improves the code diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 71896d73670fd..97c8405c7932d 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -106,8 +106,8 @@ pub trait CommandExt: Sealed { /// [POSIX fork() specification]: /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html /// [`std::env`]: mod@crate::env - /// [`Error::new`]: crate::io::Error::new - /// [`Error::other`]: crate::io::Error::other + /// [`Error::new`]: ../../../io/struct.Error.html#method.new + /// [`Error::other`]: ../../../io/struct.Error.html#method.other #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where From c6517165e4b6f7ff55edb51ff37211573ca36d29 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 11:27:51 +1000 Subject: [PATCH 03/43] Change `repr` module definition to use `path` attribute Personal preference that will be used for another module in a later commit. Purely stylistic. --- library/std/src/io/error.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 67ffc79525b3a..b6fe334b9cd83 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -13,16 +13,17 @@ pub use core::io::RawOsError; // This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. // Therefore, the packed representation is explicitly disabled for UEFI // targets, and the unpacked representation must be used instead. -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -mod repr_bitpacked; -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -use repr_bitpacked::Repr; - -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; - +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + +use self::repr::Repr; use crate::{error, fmt, result, sys}; /// A specialized [`Result`] type for I/O operations. From 1f0ccc767ae2ac5cf280eb3ba235d27fa4e5cd3b Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:32:12 +1000 Subject: [PATCH 04/43] Setup `alloc::io` --- library/alloc/src/io/mod.rs | 13 +++++++++++++ library/alloc/src/lib.rs | 6 ++++++ library/std/src/io/error.rs | 4 ++-- library/std/src/io/mod.rs | 9 +++++---- library/std/src/lib.rs | 1 + src/tools/linkchecker/main.rs | 16 ++++++++++++++++ 6 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 library/alloc/src/io/mod.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..411b3cd9e8b87 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,13 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use core::io::RawOsError; +#[unstable(feature = "core_io_borrowed_buf", issue = "117693")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use core::io::{ + Chain, Cursor, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, Sink, Take, empty, repeat, sink, +}; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use core::io::{chain, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 3f0c0ab3080c5..761884c228dd0 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -112,6 +112,9 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(diagnostic_on_move)] @@ -143,6 +146,7 @@ #![feature(ptr_cast_slice)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(raw_os_error_ty)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] #![feature(share_trait)] @@ -232,6 +236,8 @@ pub mod collections; pub mod ffi; pub mod fmt; pub mod intrinsics; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index b6fe334b9cd83..5e67fb4f1419f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -2,9 +2,9 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::ErrorKind; +pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use core::io::RawOsError; +pub use alloc_crate::io::RawOsError; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 91092579a884e..2ddd6c66091dc 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,13 +297,14 @@ #[cfg(test)] mod tests; +use core::slice::memchr; + #[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; +pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use alloc_crate::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; #[stable(feature = "iovec", since = "1.36.0")] -pub use core::io::{IoSlice, IoSliceMut}; -use core::slice::memchr; +pub use alloc_crate::io::{IoSlice, IoSliceMut}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 8086cab34cf44..6ee3679918129 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -388,6 +388,7 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e7455fc701cfb..0d3870f456a07 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -92,6 +92,22 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ "core\\io\\slice::sort_by_key", "#method.sort_by_cached_key" ]), + ("alloc/io/struct.IoSlice.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), + ("alloc/io/struct.IoSliceMut.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), ]; #[rustfmt::skip] From 53eba48df78ac19c04bc94030ca15776ec2b1416 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:12:27 +1000 Subject: [PATCH 05/43] Make common `Error` constants public and hidden They'll be moved into `core` but must be accessible from `std`. --- library/std/src/io/error.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 5e67fb4f1419f..0d31214651494 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -87,29 +87,43 @@ impl fmt::Debug for Error { } /// Common errors constants for use in std -#[allow(dead_code)] +#[doc(hidden)] impl Error { - pub(crate) const INVALID_UTF8: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - pub(crate) const READ_EXACT_EOF: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( ErrorKind::NotFound, "the number of hardware threads is not known for the target platform", ); - pub(crate) const UNSUPPORTED_PLATFORM: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - pub(crate) const WRITE_ALL_EOF: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - pub(crate) const ZERO_TIMEOUT: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - pub(crate) const NO_ADDRESSES: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); } From 0092197ade36c3d560c97998f80c6192976fd61c Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:28:38 +1000 Subject: [PATCH 06/43] Allow use of incoherence Incoherence is required to define inherit methods on `Error` from `alloc` and `std` once it is moved into `core`. This is required because: 1. `Box` is part of `Error`'s public API and that is only accessible from `alloc`. 2. `RawOsError` methods must ensure the `OsFunctions` atomic pointer is appropriately set, which can only be done from `std`. --- library/std/src/io/error.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 0d31214651494..7ac9a5a97ac31 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -75,6 +75,7 @@ pub type Result = result::Result; /// [Write]: ../../std/io/trait.Write.html /// [Seek]: ../../std/io/trait.Seek.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] pub struct Error { repr: Repr, } @@ -200,7 +201,7 @@ pub struct SimpleMessage { /// ``` #[rustc_macro_transparency = "semiopaque"] #[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { $crate::hint::must_use($crate::io::Error::from_static_message( const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, @@ -269,6 +270,7 @@ impl Error { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] #[inline(never)] + #[rustc_allow_incoherent_impl] pub fn new(kind: ErrorKind, error: E) -> Error where E: Into>, @@ -296,6 +298,7 @@ impl Error { /// let custom_error2 = Error::other(custom_error); /// ``` #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] pub fn other(error: E) -> Error where E: Into>, @@ -343,6 +346,7 @@ impl Error { /// let os_error = Error::last_os_error(); /// println!("last OS error: {os_error:?}"); /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "GetLastError")] #[doc(alias = "errno")] @@ -377,6 +381,7 @@ impl Error { /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); /// # } /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -570,6 +575,7 @@ impl Error { #[stable(feature = "io_error_inner", since = "1.3.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] + #[rustc_allow_incoherent_impl] pub fn into_inner(self) -> Option> { match self.repr.into_data() { ErrorData::Os(..) => None, @@ -650,6 +656,7 @@ impl Error { /// # } /// ``` #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] pub fn downcast(self) -> result::Result where E: error::Error + Send + Sync + 'static, From 9e42475b7bc10b00d25023ccf447e127add943a2 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:16:25 +1000 Subject: [PATCH 07/43] Make `Error::is_interrupted` public and hidden Required to allow `std` access from `core` --- library/std/src/io/error.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 7ac9a5a97ac31..512c2f2ed2edd 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -715,8 +715,10 @@ impl Error { } } + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] #[inline] - pub(crate) fn is_interrupted(&self) -> bool { + pub fn is_interrupted(&self) -> bool { match self.repr.data() { ErrorData::Os(code) => sys::io::is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, From b1b790f6091a735e552b05ac94f3647026934c4d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 14:25:03 +1000 Subject: [PATCH 08/43] Remove usage of `Box` from `Error` internals This allows `Error` to be moved into `core` while still retaining the ability to store custom error data. This may have performance implications! --- library/alloc/src/io/error.rs | 38 ++++++ library/alloc/src/io/mod.rs | 9 +- library/core/src/io/error.rs | 139 ++++++++++++++++++++- library/core/src/io/mod.rs | 3 + library/std/src/io/error.rs | 89 ++++++++----- library/std/src/io/error/repr_bitpacked.rs | 38 +++--- library/std/src/io/error/repr_unpacked.rs | 12 +- 7 files changed, 270 insertions(+), 58 deletions(-) create mode 100644 library/alloc/src/io/error.rs diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..6612804915c3d --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,38 @@ +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use crate::{ + boxed::Box, + io::{Custom, CustomOwner, ErrorKind}, +}; + +#[cfg(not(no_global_oom_handling))] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn custom_owner_from_box( + kind: ErrorKind, + error: Box, +) -> CustomOwner { + /// # Safety + /// + /// `ptr` must be valid to pass into `Box::from_raw`. + unsafe fn drop_box_raw(ptr: *mut T) { + // SAFETY + // Caller ensures `ptr` is valid to pass into `Box::from_raw`. + drop(unsafe { Box::from_raw(ptr) }) + } + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) }; + + // SAFETY: + // * `error` is valid up to a static lifetime, and owns its pointee. + // * `drop_box_raw` is safe to call for the pointer `error` exactly once. + // * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`, + // and will be stored in a `CustomOwner`. + let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) }; + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) }; + + // SAFETY: the `outer_drop` provided to `custom` is valid for itself. + unsafe { CustomOwner::from_raw(custom) } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 411b3cd9e8b87..2941198d0f551 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,7 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod error; + #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] @@ -10,4 +12,9 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{chain, take}; +pub use core::io::{Custom, CustomOwner, chain, take}; + +#[cfg(not(no_global_oom_handling))] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::error::custom_owner_from_box; diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 67cd1eebf44f2..c2c191893f9c6 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,6 +1,143 @@ #![unstable(feature = "core_io", issue = "154046")] -use crate::fmt; +use crate::{error, fmt}; + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. +#[doc(hidden)] +#[repr(align(4))] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct Custom { + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), +} + +// SAFETY: All members of `Custom` are `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for Custom {} + +// SAFETY: All members of `Custom` are `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for Custom {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl fmt::Debug for Custom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Custom").field("kind", &self.kind).field("error", self.error_ref()).finish() + } +} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for Custom { + fn drop(&mut self) { + // SAFETY: `Custom::from_raw` ensures this call is safe. + unsafe { + (self.error_drop)(self.error.as_ptr()); + } + } +} + +impl Custom { + /// # Safety + /// + /// * `error` must be valid for up to a static lifetime, and own its pointee. + /// * `error_drop` must be safe to call for the pointer `error` exactly once. + /// * `outer_drop` must be safe to call on a pointer to this instance of `Custom` + /// if it were stored within a [`CustomOwner`]. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw( + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), + ) -> Custom { + Custom { kind, error, error_drop, outer_drop } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.error; + core::mem::forget(self); + ptr + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_ref() } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_mut() } + } +} + +#[derive(Debug)] +#[repr(transparent)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +pub struct CustomOwner(crate::ptr::NonNull); + +// SAFETY: Custom is `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for CustomOwner {} + +// SAFETY: Custom is `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for CustomOwner {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for CustomOwner { + fn drop(&mut self) { + // SAFETY: `CustomOwner::from_raw` ensures this call is safe. + unsafe { + (self.0.as_ref().outer_drop)(self.0.as_ptr()); + } + } +} + +impl CustomOwner { + /// # Safety + /// + /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { + CustomOwner(custom) + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.0; + core::mem::forget(self); + ptr + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn custom_ref(&self) -> &Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_ref() } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn custom_mut(&mut self) -> &mut Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_mut() } + } +} /// The type of raw OS error codes. /// diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 43b6e09bfc4f0..fe6c6958f65cb 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -14,6 +14,9 @@ pub use self::cursor::Cursor; pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::error::{Custom, CustomOwner}; #[unstable(feature = "core_io", issue = "154046")] pub use self::io_slice::{IoSlice, IoSliceMut}; #[unstable(feature = "core_io", issue = "154046")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 512c2f2ed2edd..fc605bd070903 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -5,6 +5,7 @@ mod tests; pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; +use alloc_crate::io::{Custom, CustomOwner, custom_owner_from_box}; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are @@ -208,16 +209,6 @@ pub macro const_error($kind:expr, $message:expr $(,)?) { )) } -// As with `SimpleMessage`: `#[repr(align(4))]` here is just because -// repr_bitpacked's encoding requires it. In practice it almost certainly be -// already be this high or higher. -#[derive(Debug)] -#[repr(align(4))] -struct Custom { - kind: ErrorKind, - error: Box, -} - /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] @@ -242,6 +233,33 @@ impl From for Error { } impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + /// Creates a new I/O error from a known kind of error as well as an /// arbitrary error payload. /// @@ -275,7 +293,10 @@ impl Error { where E: Into>, { - Self::_new(kind, error.into()) + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has bee constructed from a `Box` from the `alloc` crate. + unsafe { Error::from_custom_owner(custom) } } /// Creates a new I/O error from an arbitrary error payload. @@ -303,11 +324,7 @@ impl Error { where E: Into>, { - Self::_new(ErrorKind::Other, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } + Self::new(ErrorKind::Other, error) } /// Creates a new I/O error from a known kind of error as well as a constant @@ -465,7 +482,7 @@ impl Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&*c.error), + ErrorData::Custom(c) => Some(c.error_ref()), } } @@ -539,7 +556,7 @@ impl Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&mut *c.error), + ErrorData::Custom(c) => Some(c.error_mut()), } } @@ -577,12 +594,20 @@ impl Error { #[inline] #[rustc_allow_incoherent_impl] pub fn into_inner(self) -> Option> { - match self.repr.into_data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error), - } + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) } /// Attempts to downcast the custom boxed error to `E`. @@ -661,16 +686,16 @@ impl Error { where E: error::Error + Send + Sync + 'static, { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() + if let Some(e) = self.get_ref() + && e.is::() { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() { Ok(*err) } else { // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } + unsafe { core::hint::unreachable_unchecked() } } } else { Err(self) @@ -756,7 +781,7 @@ impl fmt::Display for Error { let detail = sys::io::error_string(code); write!(fmt, "{detail} (os error {code})") } - ErrorData::Custom(ref c) => c.error.fmt(fmt), + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), ErrorData::Simple(kind) => kind.fmt(fmt), ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), } @@ -771,7 +796,7 @@ impl error::Error for Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.cause(), + ErrorData::Custom(c) => c.error_ref().cause(), } } @@ -780,7 +805,7 @@ impl error::Error for Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.source(), + ErrorData::Custom(c) => c.error_ref().source(), } } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 3b81759eb86e2..22cb331924081 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -106,7 +106,7 @@ use core::marker::PhantomData; use core::num::NonZeroUsize; use core::ptr::NonNull; -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; @@ -126,15 +126,15 @@ const TAG_SIMPLE: usize = 0b11; /// ``` #[repr(transparent)] #[rustc_insignificant_dtor] -pub(super) struct Repr(NonNull<()>, PhantomData>>); +pub(super) struct Repr(NonNull<()>, PhantomData>); // All the types `Repr` stores internally are Send + Sync, and so is it. unsafe impl Send for Repr {} unsafe impl Sync for Repr {} impl Repr { - pub(super) fn new_custom(b: Box) -> Self { - let p = Box::into_raw(b).cast::(); + pub(super) fn new_custom(b: CustomOwner) -> Self { + let p = b.into_raw().as_ptr().cast::(); // Should only be possible if an allocator handed out a pointer with // wrong alignment. debug_assert_eq!(p.addr() & TAG_MASK, 0); @@ -157,7 +157,8 @@ impl Repr { // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the // `new_unchecked` is safe. - let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + let ptr = unsafe { NonNull::new_unchecked(tagged) }; + let res = Self(ptr, PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); @@ -168,10 +169,8 @@ impl Repr { pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -185,10 +184,8 @@ impl Repr { pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -202,7 +199,8 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { // SAFETY: References are never null. - Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + let ptr = unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }; + Self(ptr, PhantomData) } #[inline] @@ -218,11 +216,13 @@ impl Repr { } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { let this = core::mem::ManuallyDrop::new(self); // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. - unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } + unsafe { + decode_repr(this.0, |p| CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p))) + } } } @@ -232,7 +232,9 @@ impl Drop for Repr { // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { - let _ = decode_repr(self.0, |p| Box::::from_raw(p)); + let _ = decode_repr(self.0, |p| { + CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p)) + }); } } } @@ -307,7 +309,7 @@ static_assert!(@usize_eq: size_of::>(), size_of::()); // `Custom` and `SimpleMessage` need to be thin pointers. static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::(), 8); static_assert!((TAG_MASK + 1).is_power_of_two()); // And they must have sufficient alignment. diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..a32ed58ce1b74 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/std/src/io/error/repr_unpacked.rs @@ -2,15 +2,15 @@ //! non-64bit targets, where the packed 64 bit representation wouldn't work, and //! would have no benefit. -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -type Inner = ErrorData>; +type Inner = ErrorData; pub(super) struct Repr(Inner); impl Repr { #[inline] - pub(super) fn new_custom(b: Box) -> Self { + pub(super) fn new_custom(b: CustomOwner) -> Self { Self(Inner::Custom(b)) } #[inline] @@ -26,7 +26,7 @@ impl Repr { Self(Inner::SimpleMessage(m)) } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { self.0 } #[inline] @@ -35,7 +35,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&*m), + Inner::Custom(m) => ErrorData::Custom(m.custom_ref()), } } #[inline] @@ -44,7 +44,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&mut *m), + Inner::Custom(m) => ErrorData::Custom(m.custom_mut()), } } } From b658c07e298cba8be8e6ded026260b69c76e726c Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 13:49:49 +1000 Subject: [PATCH 09/43] Delegate `RawOsError` functionality to `OsFunctions` Stored in a `static` `AtomicPtr` to allow definition in `core` and setting in `std`. Should be replaced with Externally Implemented Items (EII) or similar once stable. --- library/alloc/src/io/mod.rs | 5 +- library/core/src/io/error.rs | 24 +++++++ library/core/src/io/error/os_functions.rs | 38 ++++++++++++ .../core/src/io/error/os_functions_atomic.rs | 62 +++++++++++++++++++ library/core/src/io/mod.rs | 5 +- library/std/src/io/error.rs | 48 +++++++++++--- 6 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 library/core/src/io/error/os_functions.rs create mode 100644 library/core/src/io/error/os_functions_atomic.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 2941198d0f551..2935b8518a5d4 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -12,7 +12,10 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{Custom, CustomOwner, chain, take}; +pub use core::io::{ + Custom, CustomOwner, OsFunctions, chain, decode_error_kind, format_os_error, is_interrupted, + set_functions, take, +}; #[cfg(not(no_global_oom_handling))] #[doc(hidden)] diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index c2c191893f9c6..bdf0530047068 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,7 +1,31 @@ #![unstable(feature = "core_io", issue = "154046")] +#[cfg_attr(target_has_atomic_load_store = "ptr", path = "error/os_functions_atomic.rs")] +#[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] +mod os_functions; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; use crate::{error, fmt}; +#[doc(hidden)] +#[derive(Debug)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct OsFunctions { + pub format_os_error: fn(_: RawOsError, _: &mut fmt::Formatter<'_>) -> fmt::Result, + pub decode_error_kind: fn(_: RawOsError) -> ErrorKind, + pub is_interrupted: fn(_: RawOsError) -> bool, +} + +impl OsFunctions { + const DEFAULT: &'static OsFunctions = &OsFunctions { + format_os_error: |_, _| Ok(()), + decode_error_kind: |_| ErrorKind::Uncategorized, + is_interrupted: |_| false, + }; +} + // As with `SimpleMessage`: `#[repr(align(4))]` here is just because // repr_bitpacked's encoding requires it. In practice it almost certainly be // already be this high or higher. diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs new file mode 100644 index 0000000000000..ce769fa53df9a --- /dev/null +++ b/library/core/src/io/error/os_functions.rs @@ -0,0 +1,38 @@ +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn set_functions(f: &'static OsFunctions) { + // FIXME: externally implementable items may allow for weak linkage, allowing + // these methods to be overridden even when atomic pointers are not supported. +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = OsFunctions::DEFAULT; + (f.format_os_error)(errno, fmt) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = OsFunctions::DEFAULT; + (f.decode_error_kind)(errno) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn is_interrupted(errno: RawOsError) -> bool { + let f = OsFunctions::DEFAULT; + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs new file mode 100644 index 0000000000000..e870a2c5a2e81 --- /dev/null +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -0,0 +1,62 @@ +//! OS-dependent functions +//! +//! `Error` needs OS functionalities to work interpret raw OS errors, but +//! we can't link to anythink here in `alloc`. Therefore, we restrict +//! creation of `Error` from raw OS errors in `std`, and require providing +//! a vtable of operations when creating one. + +// FIXME: replace this with externally implementable items once they are more stable + +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; +use crate::sync::atomic; + +/// These default functions are not reachable, but have them just to be safe. +static OS_FUNCTIONS: atomic::AtomicPtr = + atomic::AtomicPtr::new(OsFunctions::DEFAULT as *const _ as *mut _); + +fn get_os_functions() -> &'static OsFunctions { + // SAFETY: + // * `OS_FUNCTIONS` is initially a pointer to `OsFunctions::DEFAULT`, which is valid for a static lifetime. + // * `OS_FUNCTIONS` can only be changed by `set_functions`, which only accepts `&'static OsFunctions`. + // * Therefore, `OS_FUNCTIONS` must always contain a valid non-null pointer with a static lifetime. + // * `Relaxed` ordering is sufficient as the only way to write to `OS_FUNCTIONS` is through + // `set_functions`, which has as a safety precondition that any value passed in must + // be constant and not created during runtime. + unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) } +} + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn set_functions(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = get_os_functions(); + (f.format_os_error)(errno, fmt) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = get_os_functions(); + (f.decode_error_kind)(errno) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn is_interrupted(errno: RawOsError) -> bool { + let f = get_os_functions(); + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index fe6c6958f65cb..89713241b2e79 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -16,7 +16,10 @@ pub use self::error::ErrorKind; pub use self::error::RawOsError; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::{Custom, CustomOwner}; +pub use self::error::{ + Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, + set_functions, +}; #[unstable(feature = "core_io", issue = "154046")] pub use self::io_slice::{IoSlice, IoSliceMut}; #[unstable(feature = "core_io", issue = "154046")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index fc605bd070903..633fe454314f7 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -5,7 +5,10 @@ mod tests; pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; -use alloc_crate::io::{Custom, CustomOwner, custom_owner_from_box}; +use alloc_crate::io::{ + Custom, CustomOwner, OsFunctions, custom_owner_from_box, decode_error_kind, format_os_error, + is_interrupted, set_functions, +}; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are @@ -344,6 +347,25 @@ impl Error { Self { repr: Repr::new_simple_message(msg) } } + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -403,7 +425,14 @@ impl Error { #[must_use] #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { - Error { repr: Repr::new_os(code) } + const FUNCTIONS: &'static OsFunctions = &OsFunctions { + format_os_error: |code, fmt| fmt.write_str(&sys::io::error_string(code)), + decode_error_kind: sys::io::decode_error_kind, + is_interrupted: sys::io::is_interrupted, + }; + + // SAFETY: `FUNCTIONS` is a constant and not created at runtime. + unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } /// Returns the OS error that this error represents (if any). @@ -733,7 +762,7 @@ impl Error { #[inline] pub fn kind(&self) -> ErrorKind { match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), + ErrorData::Os(code) => decode_error_kind(code), ErrorData::Custom(c) => c.kind, ErrorData::Simple(kind) => kind, ErrorData::SimpleMessage(m) => m.kind, @@ -745,7 +774,7 @@ impl Error { #[inline] pub fn is_interrupted(&self) -> bool { match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), + ErrorData::Os(code) => is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, @@ -759,8 +788,13 @@ impl fmt::Debug for Repr { ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) .finish(), ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), @@ -778,7 +812,7 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr.data() { ErrorData::Os(code) => { - let detail = sys::io::error_string(code); + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); write!(fmt, "{detail} (os error {code})") } ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), From 6960ba35aecaf873dcbeaf6cac47b441e38a12eb Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 13:57:54 +1000 Subject: [PATCH 10/43] Move `std::io::Error` and friends into `core` and `alloc` --- library/alloc/src/io/error.rs | 245 +++++- library/alloc/src/io/mod.rs | 7 +- library/alloc/src/lib.rs | 2 + library/core/src/io/error.rs | 541 +++++++++++- .../src/io/error/repr_bitpacked.rs | 0 .../src/io/error/repr_unpacked.rs | 0 library/core/src/io/mod.rs | 31 +- library/core/src/lib.rs | 1 + library/std/src/io/error.rs | 792 +----------------- library/std/src/io/mod.rs | 20 +- .../binary-op-not-allowed-issue-125631.stderr | 6 +- 11 files changed, 835 insertions(+), 810 deletions(-) rename library/{std => core}/src/io/error/repr_bitpacked.rs (100%) rename library/{std => core}/src/io/error/repr_unpacked.rs (100%) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 6612804915c3d..a4d95c8376ca2 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -1,8 +1,245 @@ +use core::{error, result}; + +use crate::boxed::Box; #[cfg_attr(no_global_oom_handling, expect(unused_imports))] -use crate::{ - boxed::Box, - io::{Custom, CustomOwner, ErrorKind}, -}; +use crate::io::CustomOwner; +#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] +use crate::io::const_error; +use crate::io::{Custom, Error, ErrorKind}; + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// Note that this function allocates memory on the heap. + /// If no extra payload is required, use the `From` conversion from + /// `ErrorKind`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// + /// // creating an error without payload (and without memory allocation) + /// let eof_error = Error::from(ErrorKind::UnexpectedEof); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] + #[inline(never)] + #[rustc_allow_incoherent_impl] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has bee constructed from a `Box` from the `alloc` crate. + unsafe { Self::from_custom_owner(custom) } + } + + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] + /// with [`ErrorKind::Other`]. + /// + /// [new]: struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] + pub fn other(error: E) -> Error + where + E: Into>, + { + Self::new(ErrorKind::Other, error) + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], + /// then this function will return [`Some`], + /// otherwise it will return [`None`]. + /// + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn into_inner(self) -> Option> { + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) + } + + /// Attempts to downcast the custom boxed error to `E`. + /// + /// If this [`Error`] contains a custom boxed error, + /// then it would attempt downcasting on the boxed error, + /// otherwise it will return [`Err`]. + /// + /// If the custom boxed error has the same type as `E`, it will return [`Ok`], + /// otherwise it will also return [`Err`]. + /// + /// This method is meant to be a convenience routine for calling + /// `Box::downcast` on the custom boxed error, returned by + /// [`Error::into_inner`][into_inner]. + /// + /// [into_inner]: struct.Error.html#method.into_inner + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// use std::io; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum E { + /// Io(io::Error), + /// SomeOtherVariant, + /// } + /// + /// impl fmt::Display for E { + /// // ... + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # todo!() + /// # } + /// } + /// impl Error for E {} + /// + /// impl From for E { + /// fn from(err: io::Error) -> E { + /// err.downcast::() + /// .unwrap_or_else(E::Io) + /// } + /// } + /// + /// impl From for io::Error { + /// fn from(err: E) -> io::Error { + /// match err { + /// E::Io(io_error) => io_error, + /// e => io::Error::new(io::ErrorKind::Other, e), + /// } + /// } + /// } + /// + /// # fn main() { + /// let e = E::SomeOtherVariant; + /// // Convert it to an io::Error + /// let io_error = io::Error::from(e); + /// // Cast it back to the original variant + /// let e = E::from(io_error); + /// assert!(matches!(e, E::SomeOtherVariant)); + /// + /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); + /// // Convert it to E + /// let e = E::from(io_error); + /// // Cast it back to the original variant + /// let io_error = io::Error::from(e); + /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); + /// assert!(io_error.get_ref().is_none()); + /// assert!(io_error.raw_os_error().is_none()); + /// # } + /// ``` + #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] + pub fn downcast(self) -> result::Result + where + E: error::Error + Send + Sync + 'static, + { + if let Some(e) = self.get_ref() + && e.is::() + { + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() + { + Ok(*err) + } else { + // Safety: We have just checked that the condition is true + unsafe { core::hint::unreachable_unchecked() } + } + } else { + Err(self) + } + } +} + +#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { + const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + +#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] +impl From for Error { + /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. + /// + /// `TryReserveError` won't be available as the error `source()`, + /// but this may change in the future. + fn from(_: crate::collections::TryReserveError) -> Error { + // ErrorData::Custom allocates, which isn't great for handling OOM errors. + ErrorKind::OutOfMemory.into() + } +} #[cfg(not(no_global_oom_handling))] #[doc(hidden)] diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 2935b8518a5d4..d868cc66b8e2a 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -4,11 +4,16 @@ mod error; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use core::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use core::io::const_error; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, Sink, Take, empty, repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, + repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 761884c228dd0..114acdfc54ec0 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -132,6 +132,8 @@ #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] +#![feature(io_const_error)] +#![feature(io_const_error_internals)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index bdf0530047068..36e91a55e5264 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,5 +1,22 @@ #![unstable(feature = "core_io", issue = "154046")] +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + #[cfg_attr(target_has_atomic_load_store = "ptr", path = "error/os_functions_atomic.rs")] #[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] mod os_functions; @@ -7,7 +24,529 @@ mod os_functions; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; -use crate::{error, fmt}; +use self::repr::Repr; +use crate::{error, fmt, result}; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This type alias is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`core::result::Result`][`Result`]. +/// +/// [`std::io`]: ../../std/io/index.html +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +/// Common errors constants for use in std +#[doc(hidden)] +impl Error { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( + ErrorKind::NotFound, + "the number of hardware threads is not known for the target platform", + ); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); +} + +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { + Os(RawOsError), + Simple(ErrorKind), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +// `#[repr(align(4))]` is probably redundant, it should have that value or +// higher already. We include it just because repr_bitpacked.rs's encoding +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +#[repr(align(4))] +#[derive(Debug)] +pub struct SimpleMessage { + pub kind: ErrorKind, + pub message: &'static str, +} + +/// Creates a new I/O error from a known kind of error and a string literal. +/// +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in +/// `const` contexts. +/// +/// [new]: ../../alloc/io/struct.Error.html#method.new +/// +/// # Example +/// ``` +/// #![feature(io_const_error)] +/// use std::io::{const_error, Error, ErrorKind}; +/// +/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); +/// +/// fn not_here() -> Result<(), Error> { +/// Err(FAIL) +/// } +/// ``` +#[rustc_macro_transparency = "semiopaque"] +#[unstable(feature = "io_const_error", issue = "133448")] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] +pub macro const_error($kind:expr, $message:expr $(,)?) { + $crate::hint::must_use($crate::io::Error::from_static_message( + const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, + )) +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion creates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{error}")); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::new_simple(kind) } + } +} + +impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. + /// + /// This function does not allocate. + /// + /// You should not use this directly, and instead use the `const_error!` + /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. + #[inline] + #[doc(hidden)] + #[unstable(feature = "io_const_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } + } + + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {raw_os_err:?}"); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn raw_os_error(&self) -> Option { + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err:?}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_ref()), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_mut()), + } + } + + /// Returns the corresponding [`ErrorKind`] for this error. + /// + /// This may be a value set by Rust code constructing custom `io::Error`s, + /// or if this `io::Error` was sourced from the operating system, + /// it will be a value inferred from the system's error encoding. + /// See [`last_os_error`][last_os_error] for more details. + /// + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // As no error has (visibly) occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> ErrorKind { + match self.repr.data() { + ErrorData::Os(code) => decode_error_kind(code), + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, + } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] + #[inline] + pub fn is_interrupted(&self) -> bool { + match self.repr.data() { + ErrorData::Os(code) => is_interrupted(code), + ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, + ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, + ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data() { + ErrorData::Os(code) => fmt + .debug_struct("Os") + .field("code", &code) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) + .finish(), + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr.data() { + ErrorData::Os(code) => { + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); + write!(fmt, "{detail} (os error {code})") + } + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), + ErrorData::Simple(kind) => kind.fmt(fmt), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} #[doc(hidden)] #[derive(Debug)] diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs similarity index 100% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/core/src/io/error/repr_bitpacked.rs diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/core/src/io/error/repr_unpacked.rs similarity index 100% rename from library/std/src/io/error/repr_unpacked.rs rename to library/core/src/io/error/repr_unpacked.rs diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 89713241b2e79..f4d7934102475 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,22 +8,25 @@ mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::cursor::Cursor; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::{ - Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, - set_functions, -}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::io_slice::{IoSlice, IoSliceMut}; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; #[unstable(feature = "core_io", issue = "154046")] -pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use self::{ + cursor::Cursor, + error::{Error, ErrorKind, Result}, + io_slice::{IoSlice, IoSliceMut}, + util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::util::{chain, take}; +pub use self::{ + error::{ + Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, + set_functions, + }, + util::{chain, take}, +}; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b16996acb1c44..0e40d7abf5f2a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -107,6 +107,7 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] +#![feature(io_const_error)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 633fe454314f7..6dbe37ca9fba8 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,371 +1,20 @@ #[cfg(test)] mod tests; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::io::ErrorKind; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use alloc_crate::io::RawOsError; -use alloc_crate::io::{ - Custom, CustomOwner, OsFunctions, custom_owner_from_box, decode_error_kind, format_os_error, - is_interrupted, set_functions, -}; - -// On 64-bit platforms, `io::Error` may use a bit-packed representation to -// reduce size. However, this representation assumes that error codes are -// always 32-bit wide. -// -// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. -// Therefore, the packed representation is explicitly disabled for UEFI -// targets, and the unpacked representation must be used instead. -#[cfg_attr( - all(target_pointer_width = "64", not(target_os = "uefi")), - path = "error/repr_bitpacked.rs" -)] #[cfg_attr( - not(all(target_pointer_width = "64", not(target_os = "uefi"))), - path = "error/repr_unpacked.rs" + test, + expect(unused, reason = "only used in implementation for non-test compilation") )] -mod repr; - -use self::repr::Repr; -use crate::{error, fmt, result, sys}; - -/// A specialized [`Result`] type for I/O operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This type alias is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`core::result::Result`][`Result`]. -/// -/// [`std::io`]: ../../std/io/index.html -/// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html -/// [Seek]: ../../std/io/trait.Seek.html -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_has_incoherent_inherent_impls] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -/// Common errors constants for use in std -#[doc(hidden)] -impl Error { - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const UNKNOWN_THREAD_COUNT: Self = const_error!( - ErrorKind::NotFound, - "the number of hardware threads is not known for the target platform", - ); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const NO_ADDRESSES: Self = - const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. - fn from(_: alloc::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") - } -} - -#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { - /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. - /// - /// `TryReserveError` won't be available as the error `source()`, - /// but this may change in the future. - fn from(_: alloc::collections::TryReserveError) -> Error { - // ErrorData::Custom allocates, which isn't great for handling OOM errors. - ErrorKind::OutOfMemory.into() - } -} - -// Only derive debug in tests, to make sure it -// doesn't accidentally get printed. -#[cfg_attr(test, derive(Debug))] -enum ErrorData { - Os(RawOsError), - Simple(ErrorKind), - SimpleMessage(&'static SimpleMessage), - Custom(C), -} - -// `#[repr(align(4))]` is probably redundant, it should have that value or -// higher already. We include it just because repr_bitpacked.rs's encoding -// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the -// alignment required by the struct, only increase it). -// -// If we add more variants to ErrorData, this can be increased to 8, but it -// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or -// whatever cfg we're using to enable the `repr_bitpacked` code, since only the -// that version needs the alignment, and 8 is higher than the alignment we'll -// have on 32 bit platforms. -// -// (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't -// matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -#[repr(align(4))] -#[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, -} - -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// [new]: ../../std/io/struct.Error.html#method.new -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semiopaque"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion creates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{error}")); - /// ``` - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::new_simple(kind) } - } -} +use crate::{ + io::{Error, OsFunctions, RawOsError}, + sys::io::{decode_error_kind, errno, error_string, is_interrupted}, +}; +// Because std is linked in during testing, these incoherent implementations would +// be duplicated if this was unconditionally included. +// See #2912 for details. +#[cfg(not(test))] impl Error { - /// # Safety - /// - /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { - Error { repr: Repr::new_custom(custom) } - } - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub fn into_custom_owner(self) -> result::Result { - if matches!(self.repr.data(), ErrorData::Custom(..)) { - let ErrorData::Custom(c) = self.repr.into_data() else { - // SAFETY: Checked above using `matches!`. - unsafe { crate::hint::unreachable_unchecked() } - }; - Ok(c) - } else { - Err(self) - } - } - - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this [`Error`]. - /// - /// Note that this function allocates memory on the heap. - /// If no extra payload is required, use the `From` conversion from - /// `ErrorKind`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// - /// // creating an error without payload (and without memory allocation) - /// let eof_error = Error::from(ErrorKind::UnexpectedEof); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] - #[inline(never)] - #[rustc_allow_incoherent_impl] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - let custom = custom_owner_from_box(kind, error.into()); - - // SAFETY: `custom_owner` has bee constructed from a `Box` from the `alloc` crate. - unsafe { Error::from_custom_owner(custom) } - } - - /// Creates a new I/O error from an arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`][new] - /// with [`ErrorKind::Other`]. - /// - /// [new]: struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// // errors can be created from strings - /// let custom_error = Error::other("oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::other(custom_error); - /// ``` - #[stable(feature = "io_error_other", since = "1.74.0")] - #[rustc_allow_incoherent_impl] - pub fn other(error: E) -> Error - where - E: Into>, - { - Self::new(ErrorKind::Other, error) - } - - /// Creates a new I/O error from a known kind of error as well as a constant - /// message. - /// - /// This function does not allocate. - /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. - /// - /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. - #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { - Self { repr: Repr::new_simple_message(msg) } - } - - /// # Safety - /// - /// `functions` must point to data that is entirely constant; it must - /// not be created during runtime. - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub unsafe fn from_raw_os_error_with_functions( - code: RawOsError, - functions: &'static OsFunctions, - ) -> Error { - // SAFETY: Caller ensures `functions` is a constant not created at runtime. - unsafe { - set_functions(functions); - } - Error { repr: Repr::new_os(code) } - } - /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -392,7 +41,7 @@ impl Error { #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) + Error::from_raw_os_error(errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -426,425 +75,12 @@ impl Error { #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { const FUNCTIONS: &'static OsFunctions = &OsFunctions { - format_os_error: |code, fmt| fmt.write_str(&sys::io::error_string(code)), - decode_error_kind: sys::io::decode_error_kind, - is_interrupted: sys::io::is_interrupted, + format_os_error: |code, fmt| fmt.write_str(&error_string(code)), + decode_error_kind, + is_interrupted, }; // SAFETY: `FUNCTIONS` is a constant and not created at runtime. unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } - - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or - /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error - /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn raw_os_error(&self) -> Option { - match self.repr.data() { - ErrorData::Os(i) => Some(i), - ErrorData::Custom(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - } - } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [new]: ../../std/io/struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err:?}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error_ref()), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [new]: ../../std/io/struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr.data_mut() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error_mut()), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [new]: struct.Error.html#method.new - /// [other]: struct.Error.html#method.other - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - #[rustc_allow_incoherent_impl] - pub fn into_inner(self) -> Option> { - let custom_owner = self.into_custom_owner().ok()?; - - let ptr = custom_owner.into_raw().as_ptr(); - - // SAFETY: - // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. - let custom = unsafe { Box::::from_raw(ptr) }; - - let ptr = custom.into_raw().as_ptr(); - - // SAFETY: - // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate - // to contain a `Custom` which itself was constructed with `Box::into_raw`. - Some(unsafe { Box::from_raw(ptr) }) - } - - /// Attempts to downcast the custom boxed error to `E`. - /// - /// If this [`Error`] contains a custom boxed error, - /// then it would attempt downcasting on the boxed error, - /// otherwise it will return [`Err`]. - /// - /// If the custom boxed error has the same type as `E`, it will return [`Ok`], - /// otherwise it will also return [`Err`]. - /// - /// This method is meant to be a convenience routine for calling - /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`][into_inner]. - /// - /// [into_inner]: struct.Error.html#method.into_inner - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// use std::io; - /// use std::error::Error; - /// - /// #[derive(Debug)] - /// enum E { - /// Io(io::Error), - /// SomeOtherVariant, - /// } - /// - /// impl fmt::Display for E { - /// // ... - /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// # todo!() - /// # } - /// } - /// impl Error for E {} - /// - /// impl From for E { - /// fn from(err: io::Error) -> E { - /// err.downcast::() - /// .unwrap_or_else(E::Io) - /// } - /// } - /// - /// impl From for io::Error { - /// fn from(err: E) -> io::Error { - /// match err { - /// E::Io(io_error) => io_error, - /// e => io::Error::new(io::ErrorKind::Other, e), - /// } - /// } - /// } - /// - /// # fn main() { - /// let e = E::SomeOtherVariant; - /// // Convert it to an io::Error - /// let io_error = io::Error::from(e); - /// // Cast it back to the original variant - /// let e = E::from(io_error); - /// assert!(matches!(e, E::SomeOtherVariant)); - /// - /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); - /// // Convert it to E - /// let e = E::from(io_error); - /// // Cast it back to the original variant - /// let io_error = io::Error::from(e); - /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); - /// assert!(io_error.get_ref().is_none()); - /// assert!(io_error.raw_os_error().is_none()); - /// # } - /// ``` - #[stable(feature = "io_error_downcast", since = "1.79.0")] - #[rustc_allow_incoherent_impl] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let Some(e) = self.get_ref() - && e.is::() - { - if let Some(b) = self.into_inner() - && let Ok(err) = b.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { core::hint::unreachable_unchecked() } - } - } else { - Err(self) - } - } - - /// Returns the corresponding [`ErrorKind`] for this error. - /// - /// This may be a value set by Rust code constructing custom `io::Error`s, - /// or if this `io::Error` was sourced from the operating system, - /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`][last_os_error] for more details. - /// - /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // As no error has (visibly) occurred, this may print anything! - /// // It likely prints a placeholder for unidentified (non-)errors. - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn kind(&self) -> ErrorKind { - match self.repr.data() { - ErrorData::Os(code) => decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - #[inline] - pub fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => is_interrupted(code), - ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, - ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, - ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data() { - ErrorData::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &decode_error_kind(code)) - .field( - "message", - &fmt::from_fn(|fmt| { - write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) - }), - ) - .finish(), - ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), - ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - ErrorData::SimpleMessage(msg) => fmt - .debug_struct("Error") - .field("kind", &msg.kind) - .field("message", &msg.message) - .finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr.data() { - ErrorData::Os(code) => { - let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), - ErrorData::Simple(kind) => kind.fmt(fmt), - ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error_ref().cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error_ref().source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 2ddd6c66091dc..36e61ce68401e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,22 +299,25 @@ mod tests; use core::slice::memchr; +use alloc_crate::io::OsFunctions; +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use alloc_crate::io::RawOsError; +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use alloc_crate::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use alloc_crate::io::const_error; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use alloc_crate::io::{ + Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, +}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; -#[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -331,7 +334,6 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index a997fbee1f2a0..ad8802bf1370f 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -12,7 +12,7 @@ note: an implementation of `PartialEq` might be missing for `T1` LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` @@ -34,7 +34,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate @@ -58,7 +58,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` From d593f3b43868dfefbff28382861ec49f973f681e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 15:27:15 +1000 Subject: [PATCH 11/43] Reduce usage of unstable public items --- library/alloc/src/io/error.rs | 11 ++++--- library/alloc/src/io/mod.rs | 10 +------ library/core/src/io/error.rs | 29 ++++++++----------- library/core/src/io/error/os_functions.rs | 16 +++------- .../core/src/io/error/os_functions_atomic.rs | 16 +++------- library/core/src/io/mod.rs | 5 +--- 6 files changed, 27 insertions(+), 60 deletions(-) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index a4d95c8376ca2..5f70ceece7015 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -1,11 +1,12 @@ +use core::io::Custom; +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use core::io::CustomOwner; use core::{error, result}; use crate::boxed::Box; -#[cfg_attr(no_global_oom_handling, expect(unused_imports))] -use crate::io::CustomOwner; #[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] use crate::io::const_error; -use crate::io::{Custom, Error, ErrorKind}; +use crate::io::{Error, ErrorKind}; impl Error { /// Creates a new I/O error from a known kind of error as well as an @@ -242,9 +243,7 @@ impl From for Error { } #[cfg(not(no_global_oom_handling))] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn custom_owner_from_box( +fn custom_owner_from_box( kind: ErrorKind, error: Box, ) -> CustomOwner { diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index d868cc66b8e2a..58e5ffa39d0c4 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -17,12 +17,4 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{ - Custom, CustomOwner, OsFunctions, chain, decode_error_kind, format_os_error, is_interrupted, - set_functions, take, -}; - -#[cfg(not(no_global_oom_handling))] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::custom_owner_from_box; +pub use core::io::{OsFunctions, chain, take}; diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 36e91a55e5264..393873c57621b 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -21,9 +21,7 @@ mod repr; #[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] mod os_functions; -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; +use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; use self::repr::Repr; use crate::{error, fmt, result}; @@ -572,8 +570,7 @@ impl OsFunctions { #[repr(align(4))] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub struct Custom { - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub kind: ErrorKind, + kind: ErrorKind, error: crate::ptr::NonNull, error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), outer_drop: unsafe fn(*mut Self), @@ -628,16 +625,14 @@ impl Custom { ptr } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { // SAFETY: // `from_raw` ensures `error` is a valid pointer up to a static lifetime // and is owned by `self` unsafe { self.error.as_ref() } } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { // SAFETY: // `from_raw` ensures `error` is a valid pointer up to a static lifetime // and is owned by `self` @@ -673,6 +668,7 @@ impl CustomOwner { /// # Safety /// /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { CustomOwner(custom) @@ -685,16 +681,16 @@ impl CustomOwner { ptr } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn custom_ref(&self) -> &Custom { + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_ref(&self) -> &Custom { // SAFETY: // `from_raw` ensures `0` is a valid pointer up to a static lifetime // and is owned by `self` unsafe { self.0.as_ref() } } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn custom_mut(&mut self) -> &mut Custom { + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_mut(&mut self) -> &mut Custom { // SAFETY: // `from_raw` ensures `0` is a valid pointer up to a static lifetime // and is owned by `self` @@ -968,7 +964,7 @@ pub enum ErrorKind { } impl ErrorKind { - pub(crate) const fn as_str(&self) -> &'static str { + const fn as_str(&self) -> &'static str { use ErrorKind::*; match *self { // tidy-alphabetical-start @@ -1022,9 +1018,8 @@ impl ErrorKind { // unsafe, or to hard-code max ErrorKind or its size in a way the compiler // couldn't verify. #[inline] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - pub const fn from_prim(ek: u32) -> Option { + #[allow(dead_code, reason = "only used for packed representation")] + const fn from_prim(ek: u32) -> Option { macro_rules! from_prim { ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ // Force a compile error if the list gets out of date. diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs index ce769fa53df9a..b34b7c7dc7926 100644 --- a/library/core/src/io/error/os_functions.rs +++ b/library/core/src/io/error/os_functions.rs @@ -6,33 +6,25 @@ use crate::fmt; /// The provided reference must point to data that is entirely constant; it must /// not be created during runtime. #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub unsafe fn set_functions(f: &'static OsFunctions) { +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { // FIXME: externally implementable items may allow for weak linkage, allowing // these methods to be overridden even when atomic pointers are not supported. } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let f = OsFunctions::DEFAULT; (f.format_os_error)(errno, fmt) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { let f = OsFunctions::DEFAULT; (f.decode_error_kind)(errno) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn is_interrupted(errno: RawOsError) -> bool { +pub(super) fn is_interrupted(errno: RawOsError) -> bool { let f = OsFunctions::DEFAULT; (f.is_interrupted)(errno) } diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs index e870a2c5a2e81..762b011c3f88c 100644 --- a/library/core/src/io/error/os_functions_atomic.rs +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -31,32 +31,24 @@ fn get_os_functions() -> &'static OsFunctions { /// The provided reference must point to data that is entirely constant; it must /// not be created during runtime. #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub unsafe fn set_functions(f: &'static OsFunctions) { +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let f = get_os_functions(); (f.format_os_error)(errno, fmt) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { let f = get_os_functions(); (f.decode_error_kind)(errno) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn is_interrupted(errno: RawOsError) -> bool { +pub(super) fn is_interrupted(errno: RawOsError) -> bool { let f = get_os_functions(); (f.is_interrupted)(errno) } diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index f4d7934102475..b3c8815cb062c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -24,9 +24,6 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - error::{ - Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, - set_functions, - }, + error::{Custom, CustomOwner, OsFunctions}, util::{chain, take}, }; From 196072bd66261b088d57073471e08cac9f4827c1 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 15:30:01 +1000 Subject: [PATCH 12/43] Simplify `core::io` documentation links Now that `Error` lives in `core::io`, doc links to it from `core` can be simplified. --- library/core/src/io/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 393873c57621b..032231ed05262 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -719,7 +719,7 @@ pub type RawOsError = cfg_select! { /// /// It is used with the [`io::Error`][error] type. /// -/// [error]: ../../std/io/struct.Error.html +/// [error]: Error /// /// # Handling errors and matching on `ErrorKind` /// @@ -949,7 +949,7 @@ pub enum ErrorKind { /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. /// New [`ErrorKind`]s might be added in the future for some of those. /// - /// [error]: ../../std/io/struct.Error.html + /// [error]: Error #[stable(feature = "rust1", since = "1.0.0")] Other, From cb843c4fda635d0ac6f8767dc7c030bfafc88156 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 21:20:35 +1000 Subject: [PATCH 13/43] Move `std::io::IoHandle` to `core::io` --- library/alloc/src/io/mod.rs | 2 +- library/core/src/io/mod.rs | 19 +++++++++++++++++++ library/std/src/fs.rs | 2 ++ library/std/src/io/mod.rs | 16 +--------------- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 58e5ffa39d0c4..8a26543d1be09 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -17,4 +17,4 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{OsFunctions, chain, take}; +pub use core::io::{IoHandle, OsFunctions, chain, take}; diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index b3c8815cb062c..9d7e528bf6e5f 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -27,3 +27,22 @@ pub use self::{ error::{Custom, CustomOwner, OsFunctions}, util::{chain, take}, }; + +/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically +/// implemented for handle types like [`Arc`][arc] as well. +/// +/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)` +/// would be identical to `::method(&mut value, ..)`. +/// +/// [`File`][file] passes this test, as operations on `&File` and `File` both affect +/// the same underlying file. +/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary +/// and be lost after the method has been called. +/// +/// [file]: ../../std/fs/struct.File.html +/// [arc]: ../../alloc/sync/struct.Arc.html +/// [`Write`]: ../../std/io/trait.Write.html +/// [`Seek`]: ../../std/io/trait.Seek.html +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait IoHandle {} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 20e28410a6294..92f1217a5147d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1541,6 +1541,8 @@ impl Seek for File { (&*self).stream_position() } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl crate::io::IoHandle for File {} impl Dir { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 36e61ce68401e..0cafb10a62aee 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,6 +299,7 @@ mod tests; use core::slice::memchr; +pub(crate) use alloc_crate::io::IoHandle; use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; @@ -1914,21 +1915,6 @@ pub enum SeekFrom { Current(#[stable(feature = "rust1", since = "1.0.0")] i64), } -/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically -/// implemented for handle types like [`Arc`][arc] as well. -/// -/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)` -/// would be identical to `::method(&mut value, ..)`. -/// -/// [`File`][file] passes this test, as operations on `&File` and `File` both affect -/// the same underlying file. -/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary -/// and be lost after the method has been called. -/// -/// [file]: crate::fs::File -/// [arc]: crate::sync::Arc -pub(crate) trait IoHandle {} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { From c7c595eb111111cf7cb9dac29d1f0fbcd8fe7835 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 21:50:01 +1000 Subject: [PATCH 14/43] Move `std::io::SizeHint` to `core::io` --- library/alloc/src/io/impls.rs | 22 ++++++ library/alloc/src/io/mod.rs | 3 +- library/core/src/io/impls.rs | 35 +++++++++ library/core/src/io/mod.rs | 3 + library/core/src/io/size_hint.rs | 25 +++++++ library/core/src/io/util.rs | 60 +++++++++++++++- library/std/src/io/buffered/bufreader.rs | 2 + library/std/src/io/mod.rs | 90 +----------------------- library/std/src/io/util.rs | 21 +----- 9 files changed, 150 insertions(+), 111 deletions(-) create mode 100644 library/alloc/src/io/impls.rs create mode 100644 library/core/src/io/impls.rs create mode 100644 library/core/src/io/size_hint.rs diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs new file mode 100644 index 0000000000000..6f392bba93ef4 --- /dev/null +++ b/library/alloc/src/io/impls.rs @@ -0,0 +1,22 @@ +use crate::boxed::Box; +use crate::io::SizeHint; + +// ============================================================================= +// Forwarding implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Box { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&**self) + } + + #[inline] + fn upper_bound(&self) -> Option { + SizeHint::upper_bound(&**self) + } +} + +// ============================================================================= +// In-memory buffer implementations diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 8a26543d1be09..86dc8c685a985 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,6 +1,7 @@ //! Traits, helpers, and type definitions for core I/O functionality. mod error; +mod impls; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -17,4 +18,4 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, chain, take}; +pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take}; diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs new file mode 100644 index 0000000000000..cc222c56121e2 --- /dev/null +++ b/library/core/src/io/impls.rs @@ -0,0 +1,35 @@ +use crate::io::SizeHint; + +// ============================================================================= +// Forwarding implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for &mut T { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(*self) + } + + #[inline] + fn upper_bound(&self) -> Option { + SizeHint::upper_bound(*self) + } +} + +// ============================================================================= +// In-memory buffer implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for &[u8] { + #[inline] + fn lower_bound(&self) -> usize { + self.len() + } + + #[inline] + fn upper_bound(&self) -> Option { + Some(self.len()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 9d7e528bf6e5f..d05f73fd26805 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -3,7 +3,9 @@ mod borrowed_buf; mod cursor; mod error; +mod impls; mod io_slice; +mod size_hint; mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] @@ -25,6 +27,7 @@ pub use self::{ #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ error::{Custom, CustomOwner, OsFunctions}, + size_hint::SizeHint, util::{chain, take}, }; diff --git a/library/core/src/io/size_hint.rs b/library/core/src/io/size_hint.rs new file mode 100644 index 0000000000000..8a5b0a63c075b --- /dev/null +++ b/library/core/src/io/size_hint.rs @@ -0,0 +1,25 @@ +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait SizeHint { + fn lower_bound(&self) -> usize; + + fn upper_bound(&self) -> Option; + + fn size_hint(&self) -> (usize, Option) { + (self.lower_bound(), self.upper_bound()) + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for T { + #[inline] + default fn lower_bound(&self) -> usize { + 0 + } + + #[inline] + default fn upper_bound(&self) -> Option { + None + } +} diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index 232e388433fb0..a40318bb85e08 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,5 @@ -use crate::fmt; +use crate::io::SizeHint; +use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. @@ -13,6 +14,15 @@ use crate::fmt; #[derive(Copy, Clone, Debug, Default)] pub struct Empty; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Empty { + #[inline] + fn upper_bound(&self) -> Option { + Some(0) + } +} + /// Creates a value that is always at EOF for reads, and ignores all data written. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] @@ -63,6 +73,20 @@ pub struct Repeat { pub byte: u8, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Repeat { + #[inline] + fn lower_bound(&self) -> usize { + usize::MAX + } + + #[inline] + fn upper_bound(&self) -> Option { + None + } +} + /// Creates an instance of a reader that infinitely repeats one byte. /// /// All reads from this reader will succeed by filling the specified buffer with @@ -145,6 +169,23 @@ pub struct Chain { pub done_first: bool, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Chain { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) + } + + #[inline] + fn upper_bound(&self) -> Option { + match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { + (Some(first), Some(second)) => first.checked_add(second), + _ => None, + } + } +} + impl Chain { /// Consumes the `Chain`, returning the wrapped readers. /// @@ -253,6 +294,23 @@ pub struct Take { pub limit: u64, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Take { + #[inline] + fn lower_bound(&self) -> usize { + cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize + } + + #[inline] + fn upper_bound(&self) -> Option { + match SizeHint::upper_bound(&self.inner) { + Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize), + None => self.limit.try_into().ok(), + } + } +} + impl Take { /// Returns the number of bytes that can be read before this instance will /// return EOF. diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 36bf329a946e0..4ff99eb92b98d 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -580,6 +580,8 @@ impl Seek for BufReader { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SizeHint for BufReader { #[inline] fn lower_bound(&self) -> usize { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 0cafb10a62aee..84014cc245c2a 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -300,7 +300,6 @@ mod tests; use core::slice::memchr; pub(crate) use alloc_crate::io::IoHandle; -use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -316,6 +315,7 @@ pub use alloc_crate::io::{ }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; +use alloc_crate::io::{OsFunctions, SizeHint}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; @@ -2472,21 +2472,6 @@ impl BufRead for Chain { // split between the two parts of the chain } -impl SizeHint for Chain { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) - } - - #[inline] - fn upper_bound(&self) -> Option { - match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { - (Some(first), Some(second)) => first.checked_add(second), - _ => None, - } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Take { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -2581,21 +2566,6 @@ impl BufRead for Take { } } -impl SizeHint for Take { - #[inline] - fn lower_bound(&self) -> usize { - cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize - } - - #[inline] - fn upper_bound(&self) -> Option { - match SizeHint::upper_bound(&self.inner) { - Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize), - None => self.limit.try_into().ok(), - } - } -} - #[stable(feature = "seek_io_take", since = "1.89.0")] impl Seek for Take { fn seek(&mut self, pos: SeekFrom) -> Result { @@ -2704,64 +2674,6 @@ fn uninlined_slow_read_byte(reader: &mut R) -> Option> { inlined_slow_read_byte(reader) } -trait SizeHint { - fn lower_bound(&self) -> usize; - - fn upper_bound(&self) -> Option; - - fn size_hint(&self) -> (usize, Option) { - (self.lower_bound(), self.upper_bound()) - } -} - -impl SizeHint for T { - #[inline] - default fn lower_bound(&self) -> usize { - 0 - } - - #[inline] - default fn upper_bound(&self) -> Option { - None - } -} - -impl SizeHint for &mut T { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(*self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(*self) - } -} - -impl SizeHint for Box { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&**self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(&**self) - } -} - -impl SizeHint for &[u8] { - #[inline] - fn lower_bound(&self) -> usize { - self.len() - } - - #[inline] - fn upper_bound(&self) -> Option { - Some(self.len()) - } -} - /// An iterator over the contents of an instance of `BufRead` split on a /// particular byte. /// diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 3f207939c7389..75ffb7752e989 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -6,7 +6,7 @@ mod tests; use crate::fmt; use crate::io::{ self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, - SizeHint, Write, + Write, }; #[stable(feature = "rust1", since = "1.0.0")] @@ -102,13 +102,6 @@ impl Seek for Empty { } } -impl SizeHint for Empty { - #[inline] - fn upper_bound(&self) -> Option { - Some(0) - } -} - #[stable(feature = "empty_write", since = "1.73.0")] impl Write for Empty { #[inline] @@ -240,18 +233,6 @@ impl Read for Repeat { } } -impl SizeHint for Repeat { - #[inline] - fn lower_bound(&self) -> usize { - usize::MAX - } - - #[inline] - fn upper_bound(&self) -> Option { - None - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Write for Sink { #[inline] From 8539914967c9e4d98619e7802a0ead2dc15d9761 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 13:39:35 +1000 Subject: [PATCH 15/43] Move `std::io::SeekFrom` to `core::io` --- library/alloc/src/io/mod.rs | 4 ++-- library/core/src/io/mod.rs | 2 ++ library/core/src/io/seek.rs | 29 +++++++++++++++++++++++++++++ library/std/src/io/mod.rs | 30 +----------------------------- 4 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 library/core/src/io/seek.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 86dc8c685a985..486f5bf330a70 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,8 +13,8 @@ pub use core::io::const_error; pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, - repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, SeekFrom, Sink, + Take, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index d05f73fd26805..d88b68c927f1c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -5,6 +5,7 @@ mod cursor; mod error; mod impls; mod io_slice; +mod seek; mod size_hint; mod util; @@ -21,6 +22,7 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, + seek::SeekFrom, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, }; #[doc(hidden)] diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs new file mode 100644 index 0000000000000..bd0f6ca7a46cd --- /dev/null +++ b/library/core/src/io/seek.rs @@ -0,0 +1,29 @@ +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +/// +/// [`Seek`]: ../../std/io/trait.Seek.html +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + #[stable(feature = "rust1", since = "1.0.0")] + Start(#[stable(feature = "rust1", since = "1.0.0")] u64), + + /// Sets the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + End(#[stable(feature = "rust1", since = "1.0.0")] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + Current(#[stable(feature = "rust1", since = "1.0.0")] i64), +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 84014cc245c2a..b4d0ad656c1a4 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -311,7 +311,7 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, SeekFrom, Sink, Take, empty, repeat, sink, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -1887,34 +1887,6 @@ pub(crate) fn stream_len_default(self_: &mut T) -> Result Ok(len) } -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] -pub enum SeekFrom { - /// Sets the offset to the provided number of bytes. - #[stable(feature = "rust1", since = "1.0.0")] - Start(#[stable(feature = "rust1", since = "1.0.0")] u64), - - /// Sets the offset to the size of this object plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - End(#[stable(feature = "rust1", since = "1.0.0")] i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - Current(#[stable(feature = "rust1", since = "1.0.0")] i64), -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { From 3912cb1f97f8024b5380f5a9f3bad317cdc7b47e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 13:52:15 +1000 Subject: [PATCH 16/43] Move `std::io::Seek` to `core::io` --- library/alloc/src/io/impls.rs | 65 +++++++++- library/alloc/src/io/mod.rs | 6 +- library/alloc/src/lib.rs | 1 + library/core/src/io/cursor.rs | 37 ++++++ library/core/src/io/impls.rs | 30 ++++- library/core/src/io/mod.rs | 3 +- library/core/src/io/seek.rs | 193 ++++++++++++++++++++++++++- library/core/src/io/util.rs | 63 ++++++++- library/std/src/io/cursor.rs | 37 +----- library/std/src/io/impls.rs | 87 +------------ library/std/src/io/mod.rs | 238 +--------------------------------- library/std/src/io/util.rs | 21 +-- library/std/src/lib.rs | 1 + 13 files changed, 395 insertions(+), 387 deletions(-) diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 6f392bba93ef4..732842bc65df7 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,5 +1,7 @@ use crate::boxed::Box; -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; // ============================================================================= // Forwarding implementations @@ -18,5 +20,66 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for Arc +where + for<'a> &'a S: Seek, + S: crate::io::IoHandle, +{ + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (&**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (&**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (&**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (&**self).seek_relative(offset) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 486f5bf330a70..44a1d1b71d164 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,9 +13,9 @@ pub use core::io::const_error; pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, SeekFrom, Sink, - Take, empty, repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, + Sink, Take, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take}; +pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 114acdfc54ec0..5b20f426a0b48 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -150,6 +150,7 @@ #![feature(ptr_metadata)] #![feature(raw_os_error_ty)] #![feature(rev_into_inner)] +#![feature(seek_stream_len)] #![feature(set_ptr_value)] #![feature(share_trait)] #![feature(sized_type_properties)] diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ebbec6c5e8210..ec09dba72606d 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,3 +1,5 @@ +use crate::io::{self, ErrorKind, SeekFrom}; + /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. /// @@ -289,3 +291,38 @@ where self.pos = other.pos; } } + +#[stable(feature = "rust1", since = "1.0.0")] +impl io::Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.set_position(n); + return Ok(n); + } + SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.position(), n), + }; + match base_pos.checked_add_signed(offset) { + Some(n) => { + self.set_position(n); + Ok(n) + } + None => Err(io::const_error!( + ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + )), + } + } + + fn stream_len(&mut self) -> io::Result { + Ok(self.get_ref().as_ref().len() as u64) + } + + fn stream_position(&mut self) -> io::Result { + Ok(self.position()) + } +} diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index cc222c56121e2..e8c716f060119 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; // ============================================================================= // Forwarding implementations @@ -17,6 +17,34 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for &mut S { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index d88b68c927f1c..3a7e8e77eadfe 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -22,13 +22,14 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, - seek::SeekFrom, + seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ error::{Custom, CustomOwner, OsFunctions}, + seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, }; diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs index bd0f6ca7a46cd..94865a1d11ecc 100644 --- a/library/core/src/io/seek.rs +++ b/library/core/src/io/seek.rs @@ -1,8 +1,197 @@ +use crate::io::Result; + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +/// +/// # Examples +/// +/// `File`s implement `Seek`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// use std::io::SeekFrom; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// +/// // move the cursor 42 bytes from the start of the file +/// f.seek(SeekFrom::Start(42))?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking can fail, for example because it might involve flushing a buffer. + /// + /// Seeking to a negative offset is considered an error. + #[stable(feature = "rust1", since = "1.0.0")] + fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. + /// + /// # Errors + /// + /// Rewinding can fail, for example because it might involve flushing a buffer. + /// + /// # Example + /// + /// ```no_run + /// use std::io::{Read, Seek, Write}; + /// use std::fs::OpenOptions; + /// + /// let mut f = OpenOptions::new() + /// .write(true) + /// .read(true) + /// .create(true) + /// .open("foo.txt")?; + /// + /// let hello = "Hello!\n"; + /// write!(f, "{hello}")?; + /// f.rewind()?; + /// + /// let mut buf = String::new(); + /// f.read_to_string(&mut buf)?; + /// assert_eq!(&buf, hello); + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "seek_rewind", since = "1.55.0")] + fn rewind(&mut self) -> Result<()> { + self.seek(SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the length of this stream (in bytes). + /// + /// The default implementation uses up to three seek operations. If this + /// method returns successfully, the seek position is unchanged (i.e. the + /// position before calling this method is the same as afterwards). + /// However, if this method returns an error, the seek position is + /// unspecified. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and using its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiple times does + /// not necessarily return the same length each time. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_stream_len)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {len} bytes long"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_stream_len", issue = "59359")] + fn stream_len(&mut self) -> Result { + stream_len_default(self) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_convenience", since = "1.51.0")] + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// such as `BufReader` to perform more efficient seeks. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// f.seek_relative(10)?; + /// assert_eq!(f.stream_position()?, 10); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_seek_relative", since = "1.80.0")] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn stream_len_default(self_: &mut T) -> Result { + let old_pos = self_.stream_position()?; + let len = self_.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self_.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) +} + /// Enumeration of possible methods to seek within an I/O object. /// /// It is used by the [`Seek`] trait. -/// -/// [`Seek`]: ../../std/io/trait.Seek.html #[derive(Copy, PartialEq, Eq, Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index a40318bb85e08..de37690436c80 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty @@ -23,6 +23,24 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_seek", since = "1.51.0")] +impl Seek for Empty { + #[inline] + fn seek(&mut self, _pos: SeekFrom) -> Result { + Ok(0) + } + + #[inline] + fn stream_len(&mut self) -> Result { + Ok(0) + } + + #[inline] + fn stream_position(&mut self) -> Result { + Ok(0) + } +} + /// Creates a value that is always at EOF for reads, and ignores all data written. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] @@ -464,6 +482,49 @@ impl Take { } } +#[stable(feature = "seek_io_take", since = "1.89.0")] +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(ErrorKind::InvalidInput.into()), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { + return Err(ErrorKind::InvalidInput.into()); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[must_use] diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 9cd6d013f472c..1630b2068f5aa 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -7,42 +7,7 @@ pub use core::io::Cursor; use crate::alloc::Allocator; use crate::cmp; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl io::Seek for Cursor -where - T: AsRef<[u8]>, -{ - fn seek(&mut self, style: SeekFrom) -> io::Result { - let (base_pos, offset) = match style { - SeekFrom::Start(n) => { - self.set_position(n); - return Ok(n); - } - SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), - SeekFrom::Current(n) => (self.position(), n), - }; - match base_pos.checked_add_signed(offset) { - Some(n) => { - self.set_position(n); - Ok(n) - } - None => Err(io::const_error!( - ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", - )), - } - } - - fn stream_len(&mut self) -> io::Result { - Ok(self.get_ref().as_ref().len() as u64) - } - - fn stream_position(&mut self) -> io::Result { - Ok(self.position()) - } -} +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index f0b7764b4cc5e..2b549f6528f51 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,7 +3,7 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; use crate::sync::Arc; use crate::{cmp, fmt, mem, str}; @@ -90,33 +90,6 @@ impl Write for &mut W { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &mut S { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -229,33 +202,6 @@ impl Write for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for Box { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -804,34 +750,3 @@ where (&**self).write_fmt(fmt) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Seek for Arc -where - for<'a> &'a S: Seek, - S: crate::io::IoHandle, -{ - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (&**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (&**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (&**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (&**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (&**self).seek_relative(offset) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b4d0ad656c1a4..93f97bffdfe4b 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,7 +299,6 @@ mod tests; use core::slice::memchr; -pub(crate) use alloc_crate::io::IoHandle; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -311,8 +310,9 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, }; +pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -1696,197 +1696,6 @@ pub trait Write { } } -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -/// -/// The stream typically has a fixed size, allowing seeking relative to either -/// end or the current offset. -/// -/// # Examples -/// -/// [`File`]s implement `Seek`: -/// -/// [`File`]: crate::fs::File -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// use std::io::SeekFrom; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// -/// // move the cursor 42 bytes from the start of the file -/// f.seek(SeekFrom::Start(42))?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// If the seek operation completed successfully, - /// this method returns the new position from the start of the stream. - /// That position can be used later with [`SeekFrom::Start`]. - /// - /// # Errors - /// - /// Seeking can fail, for example because it might involve flushing a buffer. - /// - /// Seeking to a negative offset is considered an error. - #[stable(feature = "rust1", since = "1.0.0")] - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Rewind to the beginning of a stream. - /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. - /// - /// # Errors - /// - /// Rewinding can fail, for example because it might involve flushing a buffer. - /// - /// # Example - /// - /// ```no_run - /// use std::io::{Read, Seek, Write}; - /// use std::fs::OpenOptions; - /// - /// let mut f = OpenOptions::new() - /// .write(true) - /// .read(true) - /// .create(true) - /// .open("foo.txt")?; - /// - /// let hello = "Hello!\n"; - /// write!(f, "{hello}")?; - /// f.rewind()?; - /// - /// let mut buf = String::new(); - /// f.read_to_string(&mut buf)?; - /// assert_eq!(&buf, hello); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "seek_rewind", since = "1.55.0")] - fn rewind(&mut self) -> Result<()> { - self.seek(SeekFrom::Start(0))?; - Ok(()) - } - - /// Returns the length of this stream (in bytes). - /// - /// The default implementation uses up to three seek operations. If this - /// method returns successfully, the seek position is unchanged (i.e. the - /// position before calling this method is the same as afterwards). - /// However, if this method returns an error, the seek position is - /// unspecified. - /// - /// If you need to obtain the length of *many* streams and you don't care - /// about the seek position afterwards, you can reduce the number of seek - /// operations by simply calling `seek(SeekFrom::End(0))` and using its - /// return value (it is also the stream length). - /// - /// Note that length of a stream can change over time (for example, when - /// data is appended to a file). So calling this method multiple times does - /// not necessarily return the same length each time. - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_stream_len)] - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// let len = f.stream_len()?; - /// println!("The file is currently {len} bytes long"); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_stream_len", issue = "59359")] - fn stream_len(&mut self) -> Result { - stream_len_default(self) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "seek_convenience", since = "1.51.0")] - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } - - /// Seeks relative to the current position. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but - /// doesn't return the new position which can allow some implementations - /// such as [`BufReader`] to perform more efficient seeks. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// f.seek_relative(10)?; - /// assert_eq!(f.stream_position()?, 10); - /// Ok(()) - /// } - /// ``` - /// - /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "1.80.0")] - fn seek_relative(&mut self, offset: i64) -> Result<()> { - self.seek(SeekFrom::Current(offset))?; - Ok(()) - } -} - -pub(crate) fn stream_len_default(self_: &mut T) -> Result { - let old_pos = self_.stream_position()?; - let len = self_.seek(SeekFrom::End(0))?; - - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self_.seek(SeekFrom::Start(old_pos))?; - } - - Ok(len) -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { @@ -2538,49 +2347,6 @@ impl BufRead for Take { } } -#[stable(feature = "seek_io_take", since = "1.89.0")] -impl Seek for Take { - fn seek(&mut self, pos: SeekFrom) -> Result { - let new_position = match pos { - SeekFrom::Start(v) => Some(v), - SeekFrom::Current(v) => self.position().checked_add_signed(v), - SeekFrom::End(v) => self.len.checked_add_signed(v), - }; - let new_position = match new_position { - Some(v) if v <= self.len => v, - _ => return Err(ErrorKind::InvalidInput.into()), - }; - while new_position != self.position() { - if let Some(offset) = new_position.checked_signed_diff(self.position()) { - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - break; - } - let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - } - Ok(new_position) - } - - fn stream_len(&mut self) -> Result { - Ok(self.len) - } - - fn stream_position(&mut self) -> Result { - Ok(self.position()) - } - - fn seek_relative(&mut self, offset: i64) -> Result<()> { - if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { - return Err(ErrorKind::InvalidInput.into()); - } - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - Ok(()) - } -} - /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 75ffb7752e989..167fde38cd50f 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -5,8 +5,7 @@ mod tests; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, - Write, + self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, }; #[stable(feature = "rust1", since = "1.0.0")] @@ -84,24 +83,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_seek", since = "1.51.0")] -impl Seek for Empty { - #[inline] - fn seek(&mut self, _pos: SeekFrom) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - Ok(0) - } -} - #[stable(feature = "empty_write", since = "1.73.0")] impl Write for Empty { #[inline] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6ee3679918129..5638623713968 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -372,6 +372,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(seek_stream_len)] #![feature(share_trait)] #![feature(slice_internals)] #![feature(slice_ptr_get)] From 12dbdd50996ee4c79765d8f02f3b80d59adfa265 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:28:58 +1000 Subject: [PATCH 17/43] Fix documentation links to `Seek` --- library/core/src/io/cursor.rs | 2 +- library/core/src/io/error.rs | 2 +- library/core/src/io/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ec09dba72606d..25c4c61216c78 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -22,7 +22,7 @@ use crate::io::{self, ErrorKind, SeekFrom}; /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// /// ```no_run diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 032231ed05262..2610f45399ea6 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -72,7 +72,7 @@ pub type Result = result::Result; /// /// [Read]: ../../std/io/trait.Read.html /// [Write]: ../../std/io/trait.Write.html -/// [Seek]: ../../std/io/trait.Seek.html +/// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] pub struct Error { diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 3a7e8e77eadfe..65a887acc8991 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -48,7 +48,7 @@ pub use self::{ /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub trait IoHandle {} From 737b94f9b9d3a3ee86336fa59acb1d553b22f4ea Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 22:36:50 +1000 Subject: [PATCH 18/43] Add `std::io::cursor::WriteThroughCursor` Used to work around `Cursor` not being a fundamental type when implementing `Write` for `Cursor>` and other `alloc` types once `Cursor` and `Write` are both located in `core`. --- library/std/src/io/cursor.rs | 108 ++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 33 deletions(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 1630b2068f5aa..371cf77c36553 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -102,6 +102,48 @@ where } } +trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} + // Non-resizing write implementation #[inline] fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { @@ -313,117 +355,117 @@ impl Write for Cursor<&mut [u8]> { } #[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> +impl WriteThroughCursor for &mut Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> +impl WriteThroughCursor for Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> +impl WriteThroughCursor for Box<[u8], A> where A: Allocator, { #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write(pos, inner, buf) } #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all(pos, inner, buf) } #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all_vectored(pos, inner, bufs) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } From ccf3201ee890988052b2bb51b7a3ba47f41c6fc2 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 15:40:10 +1000 Subject: [PATCH 19/43] Move `std::io::default_write_vectored` to `core::io` --- library/alloc/src/io/mod.rs | 4 +++- library/core/src/io/mod.rs | 2 ++ library/core/src/io/write.rs | 11 +++++++++++ library/std/src/io/mod.rs | 10 +--------- 4 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 library/core/src/io/write.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 44a1d1b71d164..91bf95ab589be 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -18,4 +18,6 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; +pub use core::io::{ + IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, stream_len_default, take, +}; diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 65a887acc8991..96f23c3484a38 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,6 +8,7 @@ mod io_slice; mod seek; mod size_hint; mod util; +mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; @@ -32,6 +33,7 @@ pub use self::{ seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, + write::default_write_vectored, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs new file mode 100644 index 0000000000000..cd0d2eb37249b --- /dev/null +++ b/library/core/src/io/write.rs @@ -0,0 +1,11 @@ +use crate::io::{IoSlice, Result}; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result +where + F: FnOnce(&[u8]) -> Result, +{ + let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); + write(buf) +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 93f97bffdfe4b..f456bf52e79c5 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -312,7 +312,7 @@ pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; pub use alloc_crate::io::{ Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, }; -pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; +pub(crate) use alloc_crate::io::{IoHandle, default_write_vectored, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -549,14 +549,6 @@ where read(buf) } -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { From b11844bb184ff7e4ecafc6993aa288c57241d421 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 13:58:21 +1000 Subject: [PATCH 20/43] Move `std::io::cursor::slice_write` to `core::io` Call to `<&mut [u8] as Write>::write` inlined as `Write` trait not yet moved into `core`. --- library/alloc/src/io/mod.rs | 3 ++- library/core/src/io/cursor.rs | 14 ++++++++++++++ library/core/src/io/mod.rs | 1 + library/std/src/io/cursor.rs | 12 ++---------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 91bf95ab589be..a5cb2d2cc8f95 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -19,5 +19,6 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, + stream_len_default, take, }; diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 25c4c61216c78..d5543c4f362ad 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,3 +1,4 @@ +use crate::cmp; use crate::io::{self, ErrorKind, SeekFrom}; /// A `Cursor` wraps an in-memory buffer and provides it with a @@ -292,6 +293,19 @@ where } } +// Non-resizing write implementation +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let dst = &mut slice[(pos as usize)..]; + let amt = cmp::min(buf.len(), dst.len()); + dst[..amt].copy_from_slice(&buf[..amt]); + *pos_mut += amt as u64; + Ok(amt) +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 96f23c3484a38..d0a324d1492b9 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -29,6 +29,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + cursor::slice_write, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 371cf77c36553..1eb41a7f404bf 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,8 +4,9 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; +use alloc_crate::io::slice_write; + use crate::alloc::Allocator; -use crate::cmp; use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; @@ -144,15 +145,6 @@ impl Write for Cursor { } } -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - #[inline] fn slice_write_vectored( pos_mut: &mut u64, From eb72e2aec722d6ea1de6de379282e2e35bec40b5 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 14:08:00 +1000 Subject: [PATCH 21/43] Move `std::io::cursor::slice_write_vectored` to `core::io` --- library/alloc/src/io/mod.rs | 2 +- library/core/src/io/cursor.rs | 21 ++++++++++++++++++++- library/core/src/io/mod.rs | 2 +- library/std/src/io/cursor.rs | 19 +------------------ 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index a5cb2d2cc8f95..22c1f43d15337 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -20,5 +20,5 @@ pub use core::io::{ #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, - stream_len_default, take, + slice_write_vectored, stream_len_default, take, }; diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index d5543c4f362ad..7ed4d9e61da8e 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,5 +1,5 @@ use crate::cmp; -use crate::io::{self, ErrorKind, SeekFrom}; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -306,6 +306,25 @@ pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Resul Ok(amt) } +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index d0a324d1492b9..69bd5a7af907d 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -29,7 +29,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - cursor::slice_write, + cursor::{slice_write, slice_write_vectored}, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 1eb41a7f404bf..cc6bdebfa6a54 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,7 +4,7 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use alloc_crate::io::slice_write; +use alloc_crate::io::{slice_write, slice_write_vectored}; use crate::alloc::Allocator; use crate::io::prelude::*; @@ -145,23 +145,6 @@ impl Write for Cursor { } } -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) -} - #[inline] fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { let n = slice_write(pos_mut, slice, buf)?; From 0d118fbeb1f6bfac060c3bf171a844ba5fec767d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 14:31:28 +1000 Subject: [PATCH 22/43] Move `std::io::cursor::slice_write_all` to `core::io` --- library/alloc/src/io/mod.rs | 2 +- library/core/src/io/cursor.rs | 8 ++++++++ library/core/src/io/mod.rs | 2 +- library/std/src/io/cursor.rs | 8 +------- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 22c1f43d15337..6e72a35b7052b 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -19,6 +19,6 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, + IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, slice_write_vectored, stream_len_default, take, }; diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 7ed4d9e61da8e..99b80ad5757ed 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -325,6 +325,14 @@ pub fn slice_write_vectored( Ok(nwritten) } +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 69bd5a7af907d..255618e6a823f 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -29,7 +29,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - cursor::{slice_write, slice_write_vectored}, + cursor::{slice_write, slice_write_all, slice_write_vectored}, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index cc6bdebfa6a54..8ac0a7e94329e 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,7 +4,7 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use alloc_crate::io::{slice_write, slice_write_vectored}; +use alloc_crate::io::{slice_write, slice_write_all, slice_write_vectored}; use crate::alloc::Allocator; use crate::io::prelude::*; @@ -145,12 +145,6 @@ impl Write for Cursor { } } -#[inline] -fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } -} - #[inline] fn slice_write_all_vectored( pos_mut: &mut u64, From 861e21ce6c2e8e88becbf7f9e428168e78fc19e8 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 14:31:50 +1000 Subject: [PATCH 23/43] Move `std::io::cursor::slice_write_all_vectored` to `core::io` --- library/alloc/src/io/mod.rs | 2 +- library/core/src/io/cursor.rs | 16 ++++++++++++++++ library/core/src/io/mod.rs | 2 +- library/std/src/io/cursor.rs | 19 +++---------------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 6e72a35b7052b..6b05d38125654 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -20,5 +20,5 @@ pub use core::io::{ #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, - slice_write_vectored, stream_len_default, take, + slice_write_all_vectored, slice_write_vectored, stream_len_default, take, }; diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 99b80ad5757ed..6938fbb8bbd5e 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -333,6 +333,22 @@ pub fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::R if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } } +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result<()> { + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) +} #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 255618e6a823f..bad14565cd257 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -29,7 +29,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - cursor::{slice_write, slice_write_all, slice_write_vectored}, + cursor::{slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored}, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 8ac0a7e94329e..2835a2223480d 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,7 +4,9 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use alloc_crate::io::{slice_write, slice_write_all, slice_write_vectored}; +use alloc_crate::io::{ + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, +}; use crate::alloc::Allocator; use crate::io::prelude::*; @@ -145,21 +147,6 @@ impl Write for Cursor { } } -#[inline] -fn slice_write_all_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result<()> { - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) -} - /// Reserves the required space, and pads the vec with 0s if necessary. fn reserve_and_pad( pos_mut: &mut u64, From 4f544a3356d733268feaa29ea52e5d266eeab615 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 16:20:13 +1000 Subject: [PATCH 24/43] Move `std::io::Write` to `core::io` --- library/alloc/src/io/cursor.rs | 260 +++++++++++++++ library/alloc/src/io/impls.rs | 199 ++++++++++- library/alloc/src/io/mod.rs | 8 +- library/alloc/src/lib.rs | 2 + library/core/src/io/cursor.rs | 121 ++++++- library/core/src/io/impls.rs | 147 +++++++- library/core/src/io/mod.rs | 8 +- library/core/src/io/util.rs | 158 ++++++++- library/core/src/io/write.rs | 408 ++++++++++++++++++++++- library/std/src/io/cursor.rs | 365 +------------------- library/std/src/io/impls.rs | 311 +---------------- library/std/src/io/mod.rs | 405 +--------------------- library/std/src/io/util.rs | 161 +-------- library/std/src/lib.rs | 2 + tests/ui/suggestions/issue-105645.stderr | 2 +- 15 files changed, 1311 insertions(+), 1246 deletions(-) create mode 100644 library/alloc/src/io/cursor.rs diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs new file mode 100644 index 0000000000000..21f6b8b5b3ffd --- /dev/null +++ b/library/alloc/src/io/cursor.rs @@ -0,0 +1,260 @@ +use crate::alloc::Allocator; +use crate::boxed::Box; +use crate::io::{ + self, Cursor, ErrorKind, IoSlice, WriteThroughCursor, slice_write, slice_write_all, + slice_write_all_vectored, slice_write_vectored, +}; +use crate::vec::Vec; + +/// Reserves the required space, and pads the vec with 0s if necessary. +fn reserve_and_pad( + pos_mut: &mut u64, + vec: &mut Vec, + buf_len: usize, +) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + io::const_error!( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + + // For safety reasons, we don't want these numbers to overflow + // otherwise our allocation won't be enough + let desired_cap = pos.saturating_add(buf_len); + if desired_cap > vec.capacity() { + // We want our vec's total capacity + // to have room for (pos+buf_len) bytes. Reserve allocates + // based on additional elements from the length, so we need to + // reserve the difference + cfg_select! { + no_global_oom_handling => { + vec.try_reserve(desired_cap - vec.len())?; + } + _ => { + vec.reserve(desired_cap - vec.len()); + } + } + } + // Pad if pos is above the current len. + if pos > vec.len() { + let diff = pos - vec.len(); + // Unfortunately, `resize()` would suffice but the optimiser does not + // realise the `reserve` it does can be eliminated. So we do it manually + // to eliminate that extra branch + let spare = vec.spare_capacity_mut(); + debug_assert!(spare.len() >= diff); + // Safety: we have allocated enough capacity for this. + // And we are only writing, not reading + unsafe { + spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); + vec.set_len(pos); + } + } + + Ok(pos) +} + +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize +where + A: Allocator, +{ + debug_assert!(vec.capacity() >= pos + buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; + pos + buf.len() +} + +/// Resizing `write_all` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +where + A: Allocator, +{ + let buf_len = buf.len(); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to pos + unsafe { + pos = vec_write_all_unchecked(pos, vec, buf); + if pos > vec.len() { + vec.set_len(pos); + } + }; + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +/// Resizing `write_all_vectored` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all_vectored( + pos_mut: &mut u64, + vec: &mut Vec, + bufs: &[IoSlice<'_>], +) -> io::Result +where + A: Allocator, +{ + // For safety reasons, we don't want this sum to overflow ever. + // If this saturates, the reserve should panic to avoid any unsound writing. + let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to the last pos + unsafe { + for buf in bufs { + pos = vec_write_all_unchecked(pos, vec, buf); + } + if pos > vec.len() { + vec.set_len(pos); + } + } + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +#[stable(feature = "cursor_mut_vec", since = "1.25.0")] +impl WriteThroughCursor for &mut Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl WriteThroughCursor for Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_box_slice", since = "1.5.0")] +impl WriteThroughCursor for Box<[u8], A> +where + A: Allocator, +{ + #[inline] + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + #[inline] + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 732842bc65df7..0f6bfe244dba2 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,7 +1,12 @@ +use crate::alloc::Allocator; use crate::boxed::Box; -use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::fmt; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; +use crate::vec::Vec; // ============================================================================= // Forwarding implementations @@ -20,6 +25,43 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for Box { #[inline] @@ -51,6 +93,161 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Write is implemented for `Vec` by appending to the vector. +/// The vector will grow as needed. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + ::write_all(self, buf)?; + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + cfg_select! { + no_global_oom_handling => { + self.try_reserve(len)?; + } + _ => { + self.reserve(len); + } + } + for buf in bufs { + ::write_all(self, buf)?; + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + cfg_select! { + no_global_oom_handling => { + let n = buf.len(); + self.try_reserve(n)?; + // SAFETY: + // * self and buf are non-overlapping + // * self[..len] is already initialized + // * self[len..len + n] is initialized by copy_nonoverlapping + // * len + n is within the capacity of self based on the reservation completed above + unsafe { + let len = self.len(); + let src = buf.as_ptr(); + let dst = self.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, n); + self.set_len(len + n); + } + } + _ => { + self.extend_from_slice(buf); + } + } + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + self.reserve(len); + for buf in bufs { + self.extend(&**buf); + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend(buf); + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc +where + for<'a> &'a W: Write, + W: crate::io::IoHandle, +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (&**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (&**self).write_fmt(fmt) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Seek for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 6b05d38125654..678934b91da71 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,6 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod cursor; mod error; mod impls; @@ -14,11 +15,12 @@ pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, - Sink, Take, empty, repeat, sink, + Sink, Take, Write, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, - slice_write_all_vectored, slice_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_fmt, + default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, stream_len_default, take, }; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 5b20f426a0b48..12d34caa40fc8 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -93,6 +93,7 @@ #![feature(async_iterator)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(case_ignorable)] #![feature(cast_maybe_uninit)] #![feature(cell_get_cloned)] @@ -175,6 +176,7 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(write_all_vectored)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 6938fbb8bbd5e..c7b90fb164c7c 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,5 +1,5 @@ use crate::cmp; -use crate::io::{self, ErrorKind, IoSlice, SeekFrom}; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -349,6 +349,81 @@ pub fn slice_write_all_vectored( } Ok(()) } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_array", since = "1.61.0")] +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where @@ -383,3 +458,47 @@ where Ok(self.position()) } } + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[doc(hidden)] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index e8c716f060119..b3383a08b7ff8 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,5 @@ -use crate::io::{self, Seek, SeekFrom, SizeHint}; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::{cmp, fmt, mem}; // ============================================================================= // Forwarding implementations @@ -17,6 +18,43 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for &mut S { #[inline] @@ -61,3 +99,110 @@ impl SizeHint for &[u8] { Some(self.len()) } } + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, data: &[u8]) -> io::Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += self.write(buf)?; + if self.is_empty() { + break; + } + } + + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, data: &[u8]) -> io::Result<()> { + if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[unstable(feature = "read_buf", issue = "78485")] +impl<'a> io::Write for core::io::BorrowedCursor<'a> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.capacity()); + self.append(&buf[..amt]); + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = self.write(buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index bad14565cd257..531c3c5b15e4e 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -25,16 +25,20 @@ pub use self::{ io_slice::{IoSlice, IoSliceMut}, seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, + write::Write, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - cursor::{slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored}, + cursor::{ + WriteThroughCursor, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, + }, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, - write::default_write_vectored, + write::{default_write_fmt, default_write_vectored}, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index de37690436c80..4ffd38a906f1c 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,4 @@ -use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; +use crate::io::{self, ErrorKind, IoSlice, Result, Seek, SeekFrom, SizeHint, Write}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty @@ -23,6 +23,84 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "empty_seek", since = "1.51.0")] impl Seek for Empty { #[inline] @@ -142,6 +220,84 @@ impl fmt::Debug for Repeat { #[derive(Copy, Clone, Debug, Default)] pub struct Sink; +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// Creates an instance of a writer which will successfully consume all data. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index cd0d2eb37249b..91d3d2c2f6787 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -1,4 +1,370 @@ -use crate::io::{IoSlice, Result}; +use crate::fmt; +use crate::io::{Error, IoSlice, Result}; + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adapters and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: Write::write +/// [`flush`]: Write::flush +/// [`std::io`]: crate::io +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; +/// let mut buffer = File::create("foo.txt")?; +/// +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } +/// Ok(()) +/// } +/// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: Write::write_all +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] +pub trait Write { + /// Writes a buffer into this writer, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write might not succeed, or the write may also generate an + /// error. Typically, a call to `write` represents one attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + /// + /// [`Ok(n)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, buf: &[u8]) -> Result; + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer + /// read from possibly being only partially consumed. This method must + /// behave as a call to [`write`] with the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty + /// buffer provided, or an empty one if none exists. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::IoSlice; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let data1 = [1; 8]; + /// let data2 = [15; 8]; + /// let io_slice1 = IoSlice::new(&data1); + /// let io_slice2 = IoSlice::new(&data2); + /// + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write_vectored(&[io_slice1, io_slice2])?; + /// Ok(()) + /// } + /// ``` + /// + /// [`write`]: Write::write + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + default_write_vectored(|b| self.write(b), bufs) + } + + /// Determines if this `Write`r has an efficient [`write_vectored`] + /// implementation. + /// + /// If a `Write`r does not override the default [`write_vectored`] + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + /// + /// [`write_vectored`]: Write::write_vectored + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + + /// Flushes this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write_all(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write`]. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Unlike [`write_vectored`], this takes a *mutable* reference to + /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to [`write_vectored`] were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and + /// can be reused. + /// + /// [`write_vectored`]: Write::write_vectored + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + // Guarantee that bufs is empty if it contains no data, + // to avoid calling write_vectored if there is no data to be written. + IoSlice::advance_slices(&mut bufs, 0); + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => IoSlice::advance_slices(&mut bufs, n), + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!()`] macro, and it is rare that this should + /// explicitly be called. The [`write!()`] macro should be favored to + /// invoke this method instead. + /// + /// This function internally uses the [`write_all`] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [`write_all`]: Write::write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + if let Some(s) = args.as_statically_known_str() { + self.write_all(s.as_bytes()) + } else { + default_write_fmt(self, args) + } + } + + /// Creates a "by reference" adapter for this instance of `Write`. + /// + /// The returned adapter also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] @@ -9,3 +375,43 @@ where let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); write(buf) } + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_fmt(this: &mut W, args: fmt::Arguments<'_>) -> Result<()> { + // Create a shim which translates a `Write` to a `fmt::Write` and saves off + // I/O errors, instead of discarding them. + struct Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { inner: this, error: Ok(()) }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying stream did not" + ); + } + } + } +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 2835a2223480d..36e76b2b68a1b 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,13 +4,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use alloc_crate::io::{ - slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, -}; - -use crate::alloc::Allocator; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -104,361 +99,3 @@ where self.set_position(self.position() + amt as u64); } } - -trait WriteThroughCursor: Sized { - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; - fn is_write_vectored(this: &Cursor) -> bool; - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; - fn flush(this: &mut Cursor) -> io::Result<()>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - WriteThroughCursor::write(self, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - WriteThroughCursor::write_vectored(self, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - WriteThroughCursor::is_write_vectored(self) - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - WriteThroughCursor::write_all(self, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - WriteThroughCursor::write_all_vectored(self, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - WriteThroughCursor::flush(self) - } -} - -/// Reserves the required space, and pads the vec with 0s if necessary. -fn reserve_and_pad( - pos_mut: &mut u64, - vec: &mut Vec, - buf_len: usize, -) -> io::Result { - let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_error!( - ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", - ) - })?; - - // For safety reasons, we don't want these numbers to overflow - // otherwise our allocation won't be enough - let desired_cap = pos.saturating_add(buf_len); - if desired_cap > vec.capacity() { - // We want our vec's total capacity - // to have room for (pos+buf_len) bytes. Reserve allocates - // based on additional elements from the length, so we need to - // reserve the difference - vec.reserve(desired_cap - vec.len()); - } - // Pad if pos is above the current len. - if pos > vec.len() { - let diff = pos - vec.len(); - // Unfortunately, `resize()` would suffice but the optimiser does not - // realise the `reserve` it does can be eliminated. So we do it manually - // to eliminate that extra branch - let spare = vec.spare_capacity_mut(); - debug_assert!(spare.len() >= diff); - // Safety: we have allocated enough capacity for this. - // And we are only writing, not reading - unsafe { - spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); - vec.set_len(pos); - } - } - - Ok(pos) -} - -/// Writes the slice to the vec without allocating. -/// -/// # Safety -/// -/// `vec` must have `buf.len()` spare capacity. -unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize -where - A: Allocator, -{ - debug_assert!(vec.capacity() >= pos + buf.len()); - unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; - pos + buf.len() -} - -/// Resizing `write_all` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result -where - A: Allocator, -{ - let buf_len = buf.len(); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to pos - unsafe { - pos = vec_write_all_unchecked(pos, vec, buf); - if pos > vec.len() { - vec.set_len(pos); - } - }; - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -/// Resizing `write_all_vectored` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all_vectored( - pos_mut: &mut u64, - vec: &mut Vec, - bufs: &[IoSlice<'_>], -) -> io::Result -where - A: Allocator, -{ - // For safety reasons, we don't want this sum to overflow ever. - // If this saturates, the reserve should panic to avoid any unsound writing. - let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to the last pos - unsafe { - for buf in bufs { - pos = vec_write_all_unchecked(pos, vec, buf); - } - if pos > vec.len() { - vec.set_len(pos); - } - } - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor<&mut [u8]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl WriteThroughCursor for &mut Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl WriteThroughCursor for Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl WriteThroughCursor for Box<[u8], A> -where - A: Allocator, -{ - #[inline] - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - #[inline] - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_array", since = "1.61.0")] -impl Write for Cursor<[u8; N]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 2b549f6528f51..0939e3a38af1a 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,9 +3,9 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSliceMut, Read}; use crate::sync::Arc; -use crate::{cmp, fmt, mem, str}; +use crate::{cmp, str}; // ============================================================================= // Forwarding implementations @@ -53,43 +53,6 @@ impl Read for &mut R { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut W { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -165,43 +128,6 @@ impl Read for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Box { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -362,108 +288,6 @@ impl BufRead for &[u8] { } } -/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting -/// its data. -/// -/// Note that writing updates the slice to point to the yet unwritten part. -/// The slice will be empty when it has been completely overwritten. -/// -/// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut [u8] { - #[inline] - fn write(&mut self, data: &[u8]) -> io::Result { - let amt = cmp::min(data.len(), self.len()); - let (a, b) = mem::take(self).split_at_mut(amt); - a.copy_from_slice(&data[..amt]); - *self = b; - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.write(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Write is implemented for `Vec` by appending to the vector. -/// The vector will grow as needed. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend_from_slice(buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - /// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. #[stable(feature = "vecdeque_read_write", since = "1.63.0")] impl Read for VecDeque { @@ -573,96 +397,6 @@ impl BufRead for VecDeque { } } -/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Write for VecDeque { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend(&**buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[unstable(feature = "read_buf", issue = "78485")] -impl<'a> io::Write for core::io::BorrowedCursor<'a> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.capacity()); - self.append(&buf[..amt]); - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = self.write(buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Read for Arc where @@ -709,44 +443,3 @@ where (&**self).read_buf_exact(cursor) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Write for Arc -where - for<'a> &'a W: Write, - W: crate::io::IoHandle, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (&**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (&**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (&**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (&**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (&**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (&**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (&**self).write_fmt(fmt) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index f456bf52e79c5..75e85a9659e78 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,7 +310,8 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, + repeat, sink, }; pub(crate) use alloc_crate::io::{IoHandle, default_write_vectored, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] @@ -338,7 +339,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, fmt, slice, str}; +use crate::{cmp, slice, str}; mod buffered; pub(crate) mod copy; @@ -592,47 +593,6 @@ pub(crate) fn default_read_buf_exact( Ok(()) } -pub(crate) fn default_write_fmt( - this: &mut W, - args: fmt::Arguments<'_>, -) -> Result<()> { - // Create a shim which translates a `Write` to a `fmt::Write` and saves off - // I/O errors, instead of discarding them. - struct Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adapter { inner: this, error: Ok(()) }; - match fmt::write(&mut output, args) { - Ok(()) => Ok(()), - Err(..) => { - // Check whether the error came from the underlying `Write`. - if output.error.is_err() { - output.error - } else { - // This shouldn't happen: the underlying stream did not error, - // but somehow the formatter still errored? - panic!( - "a formatting trait implementation returned an error when the underlying stream did not" - ); - } - } - } -} - /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -1329,365 +1289,6 @@ pub fn read_to_string(mut reader: R) -> Result { Ok(buf) } -/// A trait for objects which are byte-oriented sinks. -/// -/// Implementors of the `Write` trait are sometimes called 'writers'. -/// -/// Writers are defined by two required methods, [`write`] and [`flush`]: -/// -/// * The [`write`] method will attempt to write some data into the object, -/// returning how many bytes were successfully written. -/// -/// * The [`flush`] method is useful for adapters and explicit buffers -/// themselves for ensuring that all buffered data has been pushed out to the -/// 'true sink'. -/// -/// Writers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Write` -/// trait. -/// -/// [`write`]: Write::write -/// [`flush`]: Write::flush -/// [`std::io`]: self -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let data = b"some bytes"; -/// -/// let mut pos = 0; -/// let mut buffer = File::create("foo.txt")?; -/// -/// while pos < data.len() { -/// let bytes_written = buffer.write(&data[pos..])?; -/// pos += bytes_written; -/// } -/// Ok(()) -/// } -/// ``` -/// -/// The trait also provides convenience methods like [`write_all`], which calls -/// `write` in a loop until its entire input has been written. -/// -/// [`write_all`]: Write::write_all -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] -pub trait Write { - /// Writes a buffer into this writer, returning how many bytes were written. - /// - /// This function will attempt to write the entire contents of `buf`, but - /// the entire write might not succeed, or the write may also generate an - /// error. Typically, a call to `write` represents one attempt to write to - /// any wrapped object. - /// - /// Calls to `write` are not guaranteed to block waiting for data to be - /// written, and a write which would otherwise block can be indicated through - /// an [`Err`] variant. - /// - /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. - /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. - /// A return value of `Ok(0)` typically means that the underlying object is - /// no longer able to accept bytes and will likely not be able to in the - /// future as well, or that the buffer provided is empty. - /// - /// # Errors - /// - /// Each call to `write` may generate an I/O error indicating that the - /// operation could not be completed. If an error is returned then no bytes - /// in the buffer were written to this writer. - /// - /// It is **not** considered an error if the entire buffer could not be - /// written to this writer. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the - /// write operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - /// - /// [`Ok(n)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, buf: &[u8]) -> Result; - - /// Like [`write`], except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer - /// read from possibly being only partially consumed. This method must - /// behave as a call to [`write`] with the buffers concatenated would. - /// - /// The default implementation calls [`write`] with either the first nonempty - /// buffer provided, or an empty one if none exists. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::IoSlice; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let data1 = [1; 8]; - /// let data2 = [15; 8]; - /// let io_slice1 = IoSlice::new(&data1); - /// let io_slice2 = IoSlice::new(&data2); - /// - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write_vectored(&[io_slice1, io_slice2])?; - /// Ok(()) - /// } - /// ``` - /// - /// [`write`]: Write::write - #[stable(feature = "iovec", since = "1.36.0")] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - default_write_vectored(|b| self.write(b), bufs) - } - - /// Determines if this `Write`r has an efficient [`write_vectored`] - /// implementation. - /// - /// If a `Write`r does not override the default [`write_vectored`] - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - /// - /// [`write_vectored`]: Write::write_vectored - #[unstable(feature = "can_vector", issue = "69941")] - fn is_write_vectored(&self) -> bool { - false - } - - /// Flushes this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// # Errors - /// - /// It is considered an error if not all bytes could be written due to - /// I/O errors or EOF being reached. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::io::BufWriter; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = BufWriter::new(File::create("foo.txt")?); - /// - /// buffer.write_all(b"some bytes")?; - /// buffer.flush()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn flush(&mut self) -> Result<()>; - - /// Attempts to write an entire buffer into this writer. - /// - /// This method will continuously call [`write`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// If the buffer contains no data, this will never call [`write`]. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. - /// - /// [`write`]: Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// buffer.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => buf = &buf[n..], - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Attempts to write multiple buffers into this writer. - /// - /// This method will continuously call [`write_vectored`] until there is no - /// more data to be written or an error of non-[`ErrorKind::Interrupted`] - /// kind is returned. This method will not return until all buffers have - /// been successfully written or such an error occurs. The first error that - /// is not of [`ErrorKind::Interrupted`] kind generated from this method - /// will be returned. - /// - /// If the buffer contains no data, this will never call [`write_vectored`]. - /// - /// # Notes - /// - /// Unlike [`write_vectored`], this takes a *mutable* reference to - /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to - /// modify the slice to keep track of the bytes already written. - /// - /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to [`write_vectored`] were necessary. It is - /// best to understand this function as taking ownership of `bufs` and to - /// not use `bufs` afterwards. The underlying buffers, to which the - /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and - /// can be reused. - /// - /// [`write_vectored`]: Write::write_vectored - /// - /// # Examples - /// - /// ``` - /// #![feature(write_all_vectored)] - /// # fn main() -> std::io::Result<()> { - /// - /// use std::io::{Write, IoSlice}; - /// - /// let mut writer = Vec::new(); - /// let bufs = &mut [ - /// IoSlice::new(&[1]), - /// IoSlice::new(&[2, 3]), - /// IoSlice::new(&[4, 5, 6]), - /// ]; - /// - /// writer.write_all_vectored(bufs)?; - /// // Note: the contents of `bufs` is now undefined, see the Notes section. - /// - /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); - /// # Ok(()) } - /// ``` - #[unstable(feature = "write_all_vectored", issue = "70436")] - fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { - // Guarantee that bufs is empty if it contains no data, - // to avoid calling write_vectored if there is no data to be written. - IoSlice::advance_slices(&mut bufs, 0); - while !bufs.is_empty() { - match self.write_vectored(bufs) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => IoSlice::advance_slices(&mut bufs, n), - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the - /// [`format_args!()`] macro, and it is rare that this should - /// explicitly be called. The [`write!()`] macro should be favored to - /// invoke this method instead. - /// - /// This function internally uses the [`write_all`] method on - /// this trait and hence will continuously write data so long as no errors - /// are received. This also means that partial writes are not indicated in - /// this signature. - /// - /// [`write_all`]: Write::write_all - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // this call - /// write!(buffer, "{:.*}", 2, 1.234567)?; - /// // turns into this: - /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { - if let Some(s) = args.as_statically_known_str() { - self.write_all(s.as_bytes()) - } else { - default_write_fmt(self, args) - } - } - - /// Creates a "by reference" adapter for this instance of `Write`. - /// - /// The returned adapter also implements `Write` and will simply borrow this - /// current writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::Write; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// let reference = buffer.by_ref(); - /// - /// // we can use reference just like our original buffer - /// reference.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 167fde38cd50f..f213f4ed7313b 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -3,10 +3,7 @@ #[cfg(test)] mod tests; -use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, -}; +use crate::io::{self, BorrowedCursor, BufRead, Empty, IoSliceMut, Read, Repeat}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Empty { @@ -83,84 +80,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for &Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { #[inline] @@ -213,81 +132,3 @@ impl Read for Repeat { true } } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5638623713968..de1eed5fb2e0a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -318,6 +318,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -385,6 +386,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(write_all_vectored)] // tidy-alphabetical-end // // Library features (alloc): diff --git a/tests/ui/suggestions/issue-105645.stderr b/tests/ui/suggestions/issue-105645.stderr index 2b9a94d8e0ab2..2342c22cfa80a 100644 --- a/tests/ui/suggestions/issue-105645.stderr +++ b/tests/ui/suggestions/issue-105645.stderr @@ -7,7 +7,7 @@ LL | foo(&mut bref); | required by a bound introduced by this call | help: the trait `std::io::Write` is implemented for `&mut [u8]` - --> $SRC_DIR/std/src/io/impls.rs:LL:COL + --> $SRC_DIR/core/src/io/impls.rs:LL:COL note: required by a bound in `foo` --> $DIR/issue-105645.rs:8:21 | From eb62ff97dbe9ebb705a20b3670824a608c46be61 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:28:42 +1000 Subject: [PATCH 25/43] Fix documentation links to `Write` --- library/core/src/io/cursor.rs | 2 +- library/core/src/io/error.rs | 4 ++-- library/core/src/io/mod.rs | 2 +- library/core/src/io/util.rs | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index c7b90fb164c7c..bdc9ea0c2c9d4 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -22,7 +22,7 @@ use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 2610f45399ea6..98fd28e08c5f9 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -71,7 +71,7 @@ pub type Result = result::Result; /// [`ErrorKind`]. /// /// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html +/// [Write]: crate::io::Write /// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] @@ -841,7 +841,7 @@ pub enum ErrorKind { /// particular number of bytes but only a smaller number of bytes could be /// written. /// - /// [write]: ../../std/io/trait.Write.html#tymethod.write + /// [write]: crate::io::Write::write /// [`Ok(0)`]: Ok #[stable(feature = "rust1", since = "1.0.0")] WriteZero, diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 531c3c5b15e4e..07789080a746d 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -54,7 +54,7 @@ pub use self::{ /// /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index 4ffd38a906f1c..b022d876eb8f5 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -4,7 +4,7 @@ use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. /// -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Read`]: ../../std/io/trait.Read.html /// /// This struct is generally created by calling [`empty()`]. Please @@ -129,7 +129,7 @@ impl Seek for Empty { /// [`Ok(buf.len())`]: Ok /// [`Ok(0)`]: Ok /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`read`]: ../../std/io/trait.Read.html#tymethod.read /// /// # Examples @@ -303,7 +303,7 @@ impl Write for &Sink { /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`Ok(buf.len())`]: Ok /// /// # Examples From b6e71410524a865896044a9619f721c15952c848 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:25:22 +1000 Subject: [PATCH 26/43] Move `std::io::Read` internals to `alloc::io` --- library/alloc/src/io/mod.rs | 5 +++++ library/alloc/src/io/read.rs | 31 +++++++++++++++++++++++++++++++ library/alloc/src/lib.rs | 1 + library/std/src/io/mod.rs | 25 +++++-------------------- library/std/src/sys/io/mod.rs | 6 ++---- 5 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 library/alloc/src/io/read.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 678934b91da71..dd0a644d1ceb3 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -3,6 +3,7 @@ mod cursor; mod error; mod impls; +mod read; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -24,3 +25,7 @@ pub use core::io::{ default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, stream_len_default, take, }; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::read::{DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs new file mode 100644 index 0000000000000..b108f41278e83 --- /dev/null +++ b/library/alloc/src/io/read.rs @@ -0,0 +1,31 @@ +use crate::io::{BorrowedCursor, IoSliceMut, Result}; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const DEFAULT_BUF_SIZE: usize = cfg_select! { + target_os = "espidf" => { 512 }, + _ => { 8 * 1024 } +}; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result +where + F: FnOnce(&mut [u8]) -> Result, +{ + let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); + read(buf) +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> +where + F: FnOnce(&mut [u8]) -> Result, +{ + let n = read(cursor.ensure_init())?; + cursor.advance_checked(n); + Ok(()) +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 12d34caa40fc8..235301d310aaa 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -91,6 +91,7 @@ #![feature(ascii_char)] #![feature(async_fn_traits)] #![feature(async_iterator)] +#![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(can_vector)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 75e85a9659e78..8fee3169b6492 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -313,7 +313,11 @@ pub use alloc_crate::io::{ Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, repeat, sink, }; -pub(crate) use alloc_crate::io::{IoHandle, default_write_vectored, stream_len_default}; +#[allow(unused_imports, reason = "only used by certain target configurations")] +pub(crate) use alloc_crate::io::{DEFAULT_BUF_SIZE, default_read_buf}; +pub(crate) use alloc_crate::io::{ + IoHandle, default_read_vectored, default_write_vectored, stream_len_default, +}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -351,8 +355,6 @@ pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - pub(crate) use stdio::cleanup; struct Guard<'a> { @@ -542,14 +544,6 @@ pub(crate) fn default_read_to_string( unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } } -pub(crate) fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result -where - F: FnOnce(&mut [u8]) -> Result, -{ - let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); - read(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { @@ -564,15 +558,6 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } } -pub(crate) fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> -where - F: FnOnce(&mut [u8]) -> Result, -{ - let n = read(cursor.ensure_init())?; - cursor.advance_checked(n); - Ok(()) -} - pub(crate) fn default_read_buf_exact( this: &mut R, mut cursor: BorrowedCursor<'_>, diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 0158137174087..02a180f4bc295 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -29,6 +29,8 @@ mod is_terminal { mod kernel_copy; +#[allow(unused_imports, reason = "only used by certain target configurations")] +pub use alloc_crate::io::DEFAULT_BUF_SIZE; #[cfg_attr(not(target_os = "linux"), allow(unused_imports))] #[cfg(all( target_family = "unix", @@ -44,7 +46,3 @@ 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}; - -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; From e2b3594cfa0fe135add11a7b804276c794d78d65 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:44:29 +1000 Subject: [PATCH 27/43] Move `std::io::append_to_string` to `alloc::io` --- library/alloc/src/io/mod.rs | 2 +- library/alloc/src/io/read.rs | 55 +++++++++++++++++++++++++++++++++++- library/std/src/io/mod.rs | 53 ++-------------------------------- 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index dd0a644d1ceb3..e4b8ef7d32cc2 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -28,4 +28,4 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::read::{DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored}; +pub use self::read::{DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_vectored}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index b108f41278e83..123b82f1715ad 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1,4 +1,6 @@ -use crate::io::{BorrowedCursor, IoSliceMut, Result}; +use crate::io::{BorrowedCursor, Error, IoSliceMut, Result}; +use crate::string::String; +use crate::vec::Vec; // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) @@ -9,6 +11,57 @@ pub const DEFAULT_BUF_SIZE: usize = cfg_select! { _ => { 8 * 1024 } }; +struct Guard<'a> { + buf: &'a mut Vec, + len: usize, +} + +impl Drop for Guard<'_> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } +} + +// Several `read_to_string` and `read_line` methods in the standard library will +// append data into a `String` buffer, but we need to be pretty careful when +// doing this. The implementation will just call `.as_mut_vec()` and then +// delegate to a byte-oriented reading method, but we must ensure that when +// returning we never leave `buf` in a state such that it contains invalid UTF-8 +// in its bounds. +// +// To this end, we use an RAII guard (to protect against panics) which updates +// the length of the string when it is dropped. This guard initially truncates +// the string to the prior length and only after we've validated that the +// new contents are valid UTF-8 do we allow it to set a longer length. +// +// The unsafety in this function is twofold: +// +// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 +// checks. +// 2. We're passing a raw buffer to the function `f`, and it is expected that +// the function only *appends* bytes to the buffer. We'll get undefined +// behavior if existing bytes are overwritten to have non-UTF-8 data. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn append_to_string(buf: &mut String, f: F) -> Result +where + F: FnOnce(&mut Vec) -> Result, +{ + let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; + let ret = f(g.buf); + + // SAFETY: the caller promises to only append data to `buf` + let appended = unsafe { g.buf.get_unchecked(g.len..) }; + if str::from_utf8(appended).is_err() { + ret.and_then(|_| Err(Error::INVALID_UTF8)) + } else { + g.len = g.buf.len(); + ret + } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 8fee3169b6492..f6c2e7c06e80e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -316,7 +316,7 @@ pub use alloc_crate::io::{ #[allow(unused_imports, reason = "only used by certain target configurations")] pub(crate) use alloc_crate::io::{DEFAULT_BUF_SIZE, default_read_buf}; pub(crate) use alloc_crate::io::{ - IoHandle, default_read_vectored, default_write_vectored, stream_len_default, + IoHandle, append_to_string, default_read_vectored, default_write_vectored, stream_len_default, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -343,7 +343,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, slice, str}; +use crate::{cmp, slice}; mod buffered; pub(crate) mod copy; @@ -357,55 +357,6 @@ mod util; pub(crate) use stdio::cleanup; -struct Guard<'a> { - buf: &'a mut Vec, - len: usize, -} - -impl Drop for Guard<'_> { - fn drop(&mut self) { - unsafe { - self.buf.set_len(self.len); - } - } -} - -// Several `read_to_string` and `read_line` methods in the standard library will -// append data into a `String` buffer, but we need to be pretty careful when -// doing this. The implementation will just call `.as_mut_vec()` and then -// delegate to a byte-oriented reading method, but we must ensure that when -// returning we never leave `buf` in a state such that it contains invalid UTF-8 -// in its bounds. -// -// To this end, we use an RAII guard (to protect against panics) which updates -// the length of the string when it is dropped. This guard initially truncates -// the string to the prior length and only after we've validated that the -// new contents are valid UTF-8 do we allow it to set a longer length. -// -// The unsafety in this function is twofold: -// -// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 -// checks. -// 2. We're passing a raw buffer to the function `f`, and it is expected that -// the function only *appends* bytes to the buffer. We'll get undefined -// behavior if existing bytes are overwritten to have non-UTF-8 data. -pub(crate) unsafe fn append_to_string(buf: &mut String, f: F) -> Result -where - F: FnOnce(&mut Vec) -> Result, -{ - let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; - let ret = f(g.buf); - - // SAFETY: the caller promises to only append data to `buf` - let appended = unsafe { g.buf.get_unchecked(g.len..) }; - if str::from_utf8(appended).is_err() { - ret.and_then(|_| Err(Error::INVALID_UTF8)) - } else { - g.len = g.buf.len(); - ret - } -} - // Here we must serve many masters with conflicting goals: // // - avoid allocating unless necessary From a221f4554b247bb10a5bca96f04ac87c60b1f149 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:51:58 +1000 Subject: [PATCH 28/43] Move `std::io::Read` to `alloc::io` --- library/alloc/src/io/cursor.rs | 128 ++- library/alloc/src/io/impls.rs | 385 ++++++- library/alloc/src/io/mod.rs | 11 +- library/alloc/src/io/read.rs | 837 +++++++++++++- library/alloc/src/io/util.rs | 314 +++++ library/alloc/src/lib.rs | 5 + library/std/src/io/buffered/bufreader.rs | 2 + library/std/src/io/cursor.rs | 85 +- library/std/src/io/impls.rs | 338 +----- library/std/src/io/mod.rs | 1011 +---------------- library/std/src/io/stdio.rs | 2 + library/std/src/io/util.rs | 99 +- library/std/src/lib.rs | 1 + .../jump-to-def/non-local-method.rs | 4 +- 14 files changed, 1692 insertions(+), 1530 deletions(-) create mode 100644 library/alloc/src/io/util.rs diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index 21f6b8b5b3ffd..a0cafe96d5f04 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -1,11 +1,135 @@ use crate::alloc::Allocator; use crate::boxed::Box; use crate::io::{ - self, Cursor, ErrorKind, IoSlice, WriteThroughCursor, slice_write, slice_write_all, - slice_write_all_vectored, slice_write_vectored, + self, BorrowedCursor, Cursor, ErrorKind, IoSlice, IoSliceMut, Read, WriteThroughCursor, + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, }; +use crate::string::String; use crate::vec::Vec; +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Cursor +where + T: AsRef<[u8]>, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = Read::read(&mut Cursor::split(self).1, buf)?; + self.set_position(self.position() + n as u64); + Ok(n) + } + + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; + + self.set_position(self.position() + (cursor.written() - prev_written) as u64); + + Ok(()) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut nread = 0; + for buf in bufs { + let n = self.read(buf)?; + nread += n; + if n < buf.len() { + break; + } + } + Ok(nread) + } + + fn is_read_vectored(&self) -> bool { + true + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let result = Read::read_exact(&mut Cursor::split(self).1, buf); + + match result { + Ok(_) => self.set_position(self.position() + buf.len() as u64), + // The only possible error condition is EOF, so place the cursor at "EOF" + Err(_) => self.set_position(self.get_ref().as_ref().len() as u64), + } + + result + } + + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); + self.set_position(self.position() + (cursor.written() - prev_written) as u64); + + result + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let content = Cursor::split(self).1; + let len = content.len(); + buf.try_reserve(len)?; + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * src and dst are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed prior to the read + unsafe { + let count = len; + let len = buf.len(); + let src = content.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.extend_from_slice(content); + } + } + self.set_position(self.position() + len as u64); + + Ok(len) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let content = + crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; + let len = content.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * buf and content are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed prior to the read + // * content is valid UTF-8 + unsafe { + let buf = buf.as_mut_vec(); + let content = content.as_bytes(); + let count = content.len(); + let len = buf.len(); + let src = content.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.push_str(content); + } + } + + self.set_position(self.position() + len as u64); + + Ok(len) + } +} + /// Reserves the required space, and pads the vec with 0s if necessary. fn reserve_and_pad( pos_mut: &mut u64, diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 0f6bfe244dba2..be2e2bc9b27cc 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,9 +1,12 @@ +use core::cmp; + use crate::alloc::Allocator; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::collections::VecDeque; use crate::fmt; -use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; +use crate::string::String; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; use crate::vec::Vec; @@ -11,6 +14,91 @@ use crate::vec::Vec; // ============================================================================= // Forwarding implementations +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &mut R { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (**self).read_exact(buf) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Box { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (**self).read_exact(buf) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } +} #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SizeHint for Box { @@ -93,6 +181,163 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } + + #[inline] + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let amt = cmp::min(cursor.capacity(), self.len()); + let (a, b) = self.split_at(amt); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut nread = 0; + for buf in bufs { + nread += self.read(buf)?; + if self.is_empty() { + break; + } + } + + Ok(nread) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if buf.len() > self.len() { + // `read_exact` makes no promise about the content of `buf` if it + // fails so don't bother about that. + *self = &self[self.len()..]; + return Err(io::Error::READ_EXACT_EOF); + } + let (a, b) = self.split_at(buf.len()); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if buf.len() == 1 { + buf[0] = a[0]; + } else { + buf.copy_from_slice(a); + } + + *self = b; + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; + return Err(io::Error::READ_EXACT_EOF); + } + let (a, b) = self.split_at(cursor.capacity()); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let len = self.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * self and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + unsafe { + let count = len; + let len = buf.len(); + let src = self.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.extend_from_slice(*self); + } + } + + *self = &self[len..]; + Ok(len) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; + let len = self.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * buf and content are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + // * content is valid UTF-8 + unsafe { + let buf = buf.as_mut_vec(); + let content = content.as_bytes(); + let count = content.len(); + let len = buf.len(); + let src = content.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.push_str(content); + } + } + + *self = &self[len..]; + Ok(len) + } +} + /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] @@ -163,6 +408,97 @@ impl Write for Vec { } } +/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Read for VecDeque { + /// Fill `buf` with the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `read` will be needed to read the entire content. + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (ref mut front, _) = self.as_slices(); + let n = Read::read(front, buf)?; + self.drain(..n); + Ok(n) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let (front, back) = self.as_slices(); + + // Use only the front buffer if it is big enough to fill `buf`, else use + // the back buffer too. + match buf.split_at_mut_checked(front.len()) { + None => buf.copy_from_slice(&front[..buf.len()]), + Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { + Some((back, _)) => { + buf_front.copy_from_slice(front); + buf_back.copy_from_slice(back); + } + None => { + self.clear(); + return Err(io::Error::READ_EXACT_EOF); + } + }, + } + + self.drain(..buf.len()); + Ok(()) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + let (ref mut front, _) = self.as_slices(); + let n = cmp::min(cursor.capacity(), front.len()); + Read::read_buf(front, cursor)?; + self.drain(..n); + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let len = cursor.capacity(); + let (front, back) = self.as_slices(); + + match front.split_at_checked(cursor.capacity()) { + Some((front, _)) => cursor.append(front), + None => { + cursor.append(front); + match back.split_at_checked(cursor.capacity()) { + Some((back, _)) => cursor.append(back), + None => { + cursor.append(back); + self.clear(); + return Err(io::Error::READ_EXACT_EOF); + } + } + } + } + + self.drain(..len); + Ok(()) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + // The total len is known upfront so we can reserve it in a single call. + let len = self.len(); + buf.try_reserve(len)?; + + let (front, back) = self.as_slices(); + buf.extend_from_slice(front); + buf.extend_from_slice(back); + self.clear(); + Ok(len) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + // SAFETY: We only append to the buffer + unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } + } +} /// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. #[cfg(not(no_global_oom_handling))] #[stable(feature = "vecdeque_read_write", since = "1.63.0")] @@ -206,6 +542,53 @@ impl Write for VecDeque { } } +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Read for Arc +where + for<'a> &'a R: Read, + R: crate::io::IoHandle, +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&**self).read(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (&**self).read_buf(cursor) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (&**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (&**self).is_read_vectored() + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (&**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (&**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (&**self).read_exact(buf) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (&**self).read_buf_exact(cursor) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Write for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index e4b8ef7d32cc2..233abb49137c8 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -4,6 +4,7 @@ mod cursor; mod error; mod impls; mod read; +mod util; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -26,6 +27,14 @@ pub use core::io::{ slice_write_vectored, stream_len_default, take, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{read::Read, util::Bytes}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::read::{DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_vectored}; +pub use self::{ + read::{ + DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, + default_read_exact, default_read_to_end, default_read_to_string, default_read_vectored, + }, + util::{SpecReadByte, bytes, uninlined_slow_read_byte}, +}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 123b82f1715ad..046ef97c06550 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1,7 +1,635 @@ -use crate::io::{BorrowedCursor, Error, IoSliceMut, Result}; +use core::cmp; +use core::mem::MaybeUninit; + +use crate::io::{ + BorrowedBuf, BorrowedCursor, Bytes, Chain, Error, IoSliceMut, Result, Take, bytes, chain, take, +}; use crate::string::String; use crate::vec::Vec; +/// The `Read` trait allows for reading bytes from a source. +/// +/// Implementors of the `Read` trait are called 'readers'. +/// +/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] +/// will attempt to pull bytes from this source into a provided buffer. A +/// number of other methods are implemented in terms of [`read()`], giving +/// implementors a number of ways to read bytes while only needing to implement +/// a single method. +/// +/// Readers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Read` +/// trait. +/// +/// Please note that each call to [`read()`] may involve a system call, and +/// therefore, using something that implements `BufRead`, such as +/// `BufReader`, will be more efficient. +/// +/// Repeated calls to the reader use the same cursor, so for example +/// calling `read_to_end` twice on a `File` will only return the file's +/// contents once. It's recommended to first call `rewind()` in that case. +/// +/// # Examples +/// +/// `File`s implement `Read`: +/// +/// ```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 +/// f.read(&mut buffer)?; +/// +/// let mut buffer = Vec::new(); +/// // read the whole file +/// f.read_to_end(&mut buffer)?; +/// +/// // read into a String, so that you don't need to do the conversion. +/// let mut buffer = String::new(); +/// f.read_to_string(&mut buffer)?; +/// +/// // and more! See the other methods for more details. +/// Ok(()) +/// } +/// ``` +/// +/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: +/// +/// ```no_run +/// # use std::io; +/// use std::io::prelude::*; +/// +/// fn main() -> io::Result<()> { +/// let mut b = "This string will be read".as_bytes(); +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// b.read(&mut buffer)?; +/// +/// // etc... it works exactly as a File does! +/// Ok(()) +/// } +/// ``` +/// +/// [`read()`]: Read::read +/// [`&str`]: prim@str +/// [`std::io`]: crate::io +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + /// + /// This function does not provide any guarantees about whether it blocks + /// waiting for data, but if an object needs to block for a read and cannot, + /// it will typically signal this via an [`Err`] return value. + /// + /// If the return value of this method is [`Ok(n)`], then implementations must + /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// that the buffer `buf` has been filled in with `n` bytes of data from this + /// source. If `n` is `0`, then it can indicate one of two scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer + /// be able to produce bytes. Note that this does not mean that the + /// reader will *always* no longer be able to produce bytes. As an example, + /// on Linux, this method will call the `recv` syscall for a `TcpStream`, + /// where returning zero indicates the connection was shut down correctly. While + /// for `File`, it is possible to reach the end of file and get zero as result, + /// but if more data is appended to the file, future calls to `read` will return + /// more data. + /// 2. The buffer specified was 0 bytes in length. + /// + /// It is not an error if the returned value `n` is smaller than the buffer size, + /// even when the reader is not at the end of the stream yet. + /// This may happen for example because fewer bytes are actually available right now + /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. + /// + /// As this trait is safe to implement, callers in unsafe code cannot rely on + /// `n <= buf.len()` for safety. + /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. + /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if + /// `n > buf.len()`. + /// + /// *Implementations* of this method can make no assumptions about the contents of `buf` when + /// this function is called. It is recommended that implementations only write data to `buf` + /// instead of reading its contents. + /// + /// Correspondingly, however, *callers* of this method in unsafe code must not assume + /// any guarantees about how the implementation uses `buf`. The trait is safe to implement, + /// so it is possible that the code that's supposed to write to the buffer might also read + /// from it. It is your responsibility to make sure that `buf` is initialized + /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one + /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. + /// + /// [`MaybeUninit`]: core::mem::MaybeUninit + /// + /// # Errors + /// + /// If this function encounters any form of I/O or other error, an error + /// variant will be returned. If an error is returned then it must be + /// guaranteed that no bytes were read. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read + /// operation should be retried if there is nothing else to do. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(n)`]: Ok + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// ```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(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Like `read`, except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer + /// written to possibly being only partially filled. This method must + /// behave equivalently to a single call to `read` with concatenated + /// buffers. + /// + /// The default implementation calls `read` with either the first nonempty + /// buffer provided, or an empty one if none exists. + #[stable(feature = "iovec", since = "1.36.0")] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + default_read_vectored(|b| self.read(b), bufs) + } + + /// Determines if this `Read`er has an efficient `read_vectored` + /// implementation. + /// + /// If a `Read`er does not override the default `read_vectored` + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + #[unstable(feature = "can_vector", issue = "69941")] + fn is_read_vectored(&self) -> bool { + false + } + + /// Reads all bytes until EOF in this source, placing them into `buf`. + /// + /// All bytes read from this source will be appended to the specified buffer + /// `buf`. This function will continuously call [`read()`] to append more data to + /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of + /// non-[`ErrorKind::Interrupted`] kind. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If any other read error is encountered then this function immediately + /// returns. Any bytes which have already been read will be appended to + /// `buf`. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(0)`]: Ok + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`read()`]: Read::read + /// + /// ```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 = Vec::new(); + /// + /// // read the whole file + /// f.read_to_end(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the `std::fs::read` convenience function for reading from a + /// file.) + /// + /// ## Implementing `read_to_end` + /// + /// When implementing the `io::Read` trait, it is recommended to allocate + /// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed + /// by all implementations, and `read_to_end` may not handle out-of-memory + /// situations gracefully. + /// + /// ```no_run + /// # #![expect(dead_code)] + /// # use std::io::{self, BufRead}; + /// # struct Example { example_datasource: io::Empty } impl Example { + /// # fn get_some_data_for_the_example(&self) -> &'static [u8] { &[] } + /// fn read_to_end(&mut self, dest_vec: &mut Vec) -> io::Result { + /// let initial_vec_len = dest_vec.len(); + /// loop { + /// let src_buf = self.example_datasource.fill_buf()?; + /// if src_buf.is_empty() { + /// break; + /// } + /// dest_vec.try_reserve(src_buf.len())?; + /// dest_vec.extend_from_slice(src_buf); + /// + /// // Any irreversible side effects should happen after `try_reserve` succeeds, + /// // to avoid losing data on allocation error. + /// let read = src_buf.len(); + /// self.example_datasource.consume(read); + /// } + /// Ok(dest_vec.len() - initial_vec_len) + /// } + /// # } + /// ``` + /// + /// # Usage Notes + /// + /// `read_to_end` attempts to read a source until EOF, but many sources are continuous streams + /// that do not send EOF. In these cases, `read_to_end` will block indefinitely. Standard input + /// is one such stream which may be finite if piped, but is typically continuous. For example, + /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. + /// Reading user input or running programs that remain open indefinitely will never terminate + /// the stream with `EOF` (e.g. `yes | my-rust-program`). + /// + /// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution + /// + /// [`read`]: Read::read + /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + default_read_to_end(self, buf, None) + } + + /// Reads all bytes until EOF in this source, appending them to `buf`. + /// + /// If successful, this function returns the number of bytes which were read + /// and appended to `buf`. + /// + /// # Errors + /// + /// If the data in this stream is *not* valid UTF-8 then an error is + /// returned and `buf` is unchanged. + /// + /// See [`read_to_end`] for other error semantics. + /// + /// [`read_to_end`]: Read::read_to_end + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```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 = String::new(); + /// + /// f.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the `std::fs::read_to_string` convenience function for + /// reading from a file.) + /// + /// # Usage Notes + /// + /// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams + /// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input + /// is one such stream which may be finite if piped, but is typically continuous. For example, + /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. + /// Reading user input or running programs that remain open indefinitely will never terminate + /// the stream with `EOF` (e.g. `yes | my-rust-program`). + /// + /// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution + /// + /// [`read`]: Read::read + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + default_read_to_string(self, buf, None) + } + + /// Reads the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the + /// specified buffer `buf`. + /// + /// *Implementations* of this method can make no assumptions about the contents of `buf` when + /// this function is called. It is recommended that implementations only write data to `buf` + /// instead of reading its contents. The documentation on [`read`] has a more detailed + /// explanation of this subject. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + /// [`read`]: Read::read + /// + /// ```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 exactly 10 bytes + /// f.read_exact(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "read_exact", since = "1.6.0")] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + default_read_exact(self, buf) + } + + /// Pull some bytes from this source into the specified buffer. + /// + /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use + /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`. + /// + /// The default implementation delegates to `read`. + /// + /// This method makes it possible to return both data and an error but it is advised against. + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + default_read_buf(|b| self.read(b), buf) + } + + /// Reads the exact number of bytes required to fill `cursor`. + /// + /// This is similar to the [`read_exact`](Read::read_exact) method, except + /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use + /// with uninitialized buffers. + /// + /// # Errors + /// + /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] + /// then the error is ignored and the operation will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// If any other read error is encountered then this function immediately + /// returns. + /// + /// If this function returns an error, all bytes read will be appended to `cursor`. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + default_read_buf_exact(self, cursor) + } + + /// Creates a "by reference" adapter for this instance of `Read`. + /// + /// The returned adapter also implements `Read` and will simply borrow this + /// current reader. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```no_run + /// use std::io; + /// use std::io::Read; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// let mut other_buffer = Vec::new(); + /// + /// { + /// let reference = f.by_ref(); + /// + /// // read at most 5 bytes + /// reference.take(5).read_to_end(&mut buffer)?; + /// + /// } // drop our &mut reference so we can use f again + /// + /// // original file still usable, read the rest + /// f.read_to_end(&mut other_buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + /// Transforms this `Read` instance to an [`Iterator`] over its bytes. + /// + /// The returned type implements [`Iterator`] where the [`Item`] is + /// [Result]<[u8], [io::Error]>. + /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] + /// otherwise. EOF is mapped to returning [`None`] from this iterator. + /// + /// The default implementation calls `read` for each byte, + /// which can be very inefficient for data that's not in memory, + /// such as `File`. Consider using a `BufReader` in such cases. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Item`]: Iterator::Item + /// [Result]: core::result::Result "Result" + /// [io::Error]: crate::io::Error "io::Error" + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = BufReader::new(File::open("foo.txt")?); + /// + /// for byte in f.bytes() { + /// println!("{}", byte?); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn bytes(self) -> Bytes + where + Self: Sized, + { + bytes(self) + } + + /// Creates an adapter which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f1 = File::open("foo.txt")?; + /// let f2 = File::open("bar.txt")?; + /// + /// let mut handle = f1.chain(f2); + /// let mut buffer = String::new(); + /// + /// // read the value into a String. We could use any Read method here, + /// // this is just one example. + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn chain(self, next: R) -> Chain + where + Self: Sized, + { + chain(self, next) + } + + /// Creates an adapter which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + /// read errors will not count towards the number of bytes read and future + /// calls to [`read()`] may succeed. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(0)`]: Ok + /// [`read()`]: Read::read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// let mut buffer = [0; 5]; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// + /// handle.read(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + take(self, limit) + } + + /// Read and return a fixed array of bytes from this source. + /// + /// This function uses an array sized based on a const generic size known at compile time. You + /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference + /// determine the number of bytes needed based on how the return value gets used. For instance, + /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of + /// bytes into an integer of the same size. + /// + /// Like `read_exact`, if this function encounters an "end of file" before reading the desired + /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + /// + /// ``` + /// #![feature(read_array)] + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); + /// let x = u64::from_le_bytes(buf.read_array()?); + /// let y = u32::from_be_bytes(buf.read_array()?); + /// let z = u16::from_be_bytes(buf.read_array()?); + /// assert_eq!(x, 0x807060504030201); + /// assert_eq!(y, 0x9080706); + /// assert_eq!(z, 0x504); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_array", issue = "148848")] + fn read_array(&mut self) -> Result<[u8; N]> + where + Self: Sized, + { + let mut buf = [MaybeUninit::uninit(); N]; + let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); + self.read_buf_exact(borrowed_buf.unfilled())?; + // Guard against incorrect `read_buf_exact` implementations. + assert_eq!(borrowed_buf.len(), N); + Ok(unsafe { MaybeUninit::array_assume_init(buf) }) + } +} + // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) #[doc(hidden)] @@ -62,6 +690,175 @@ where } } +// Here we must serve many masters with conflicting goals: +// +// - avoid allocating unless necessary +// - avoid overallocating if we know the exact size (#89165) +// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) +// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads +// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems +// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) +// +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_to_end( + r: &mut R, + buf: &mut Vec, + size_hint: Option, +) -> Result { + let start_len = buf.len(); + let start_cap = buf.capacity(); + // Optionally limit the maximum bytes read on each iteration. + // This adds an arbitrary fiddle factor to allow for more data than we expect. + let mut max_read_size = size_hint + .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) + .unwrap_or(DEFAULT_BUF_SIZE); + + const PROBE_SIZE: usize = 32; + + fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { + let mut probe = [0u8; PROBE_SIZE]; + + loop { + cfg_select! { + no_global_oom_handling => { + // Without global OOM handling we must proactively allocate the buffer + // to avoid failing after already reading data. + buf.try_reserve(PROBE_SIZE)?; + } + _ => {} + } + + match r.read(&mut probe) { + Ok(n) => { + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * probe and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..n] is initialized by copy_nonoverlapping + // * len + n is within the capacity of buf based on the reservation completed prior to the read + unsafe { + let len = buf.len(); + let src = probe.as_slice().as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, n); + buf.set_len(len + n); + } + } + _ => { + // there is no way to recover from allocation failure here + // because the data has already been read. + buf.extend_from_slice(&probe[..n]); + } + } + return Ok(n); + } + Err(ref e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + } + } + + // avoid inflating empty/small vecs before we have determined that there's anything to read + if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(0); + } + } + + loop { + if buf.len() == buf.capacity() && buf.capacity() == start_cap { + // The buffer might be an exact fit. Let's read into a probe buffer + // and see if it returns `Ok(0)`. If so, we've avoided an + // unnecessary doubling of the capacity. But if not, append the + // probe buffer to the primary buffer and let its capacity grow. + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(buf.len() - start_len); + } + } + + if buf.len() == buf.capacity() { + // buf is full, need more space + buf.try_reserve(PROBE_SIZE)?; + } + + let mut spare = buf.spare_capacity_mut(); + let buf_len = cmp::min(spare.len(), max_read_size); + spare = &mut spare[..buf_len]; + let mut read_buf: BorrowedBuf<'_> = spare.into(); + + // Note that we don't track already initialized bytes here, but this is fine + // because we explicitly limit the read size + let mut cursor = read_buf.unfilled(); + let result = loop { + match r.read_buf(cursor.reborrow()) { + Err(e) if e.is_interrupted() => continue, + // Do not stop now in case of error: we might have received both data + // and an error + res => break res, + } + }; + + let bytes_read = cursor.written(); + let is_init = read_buf.is_init(); + + // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. + unsafe { + let new_len = bytes_read + buf.len(); + buf.set_len(new_len); + } + + // Now that all data is pushed to the vector, we can fail without data loss + result?; + + if bytes_read == 0 { + return Ok(buf.len() - start_len); + } + + // Use heuristics to determine the max read size if no initial size hint was provided + if size_hint.is_none() { + // The reader is returning short reads but it doesn't call ensure_init(). + // In that case we no longer need to restrict read sizes to avoid + // initialization costs. + // When reading from disk we usually don't get any short reads except at EOF. + // So we wait for at least 2 short reads before uncapping the read buffer; + // this helps with the Windows issue. + if !is_init { + max_read_size = usize::MAX; + } + // we have passed a larger buffer than previously and the + // reader still hasn't returned a short read + else if buf_len >= max_read_size && bytes_read == buf_len { + max_read_size = max_read_size.saturating_mul(2); + } + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_to_string( + r: &mut R, + buf: &mut String, + size_hint: Option, +) -> Result { + // Note that we do *not* call `r.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `default_read_to_end` implementation which + // we know is guaranteed to only read data into the end of the buffer. + unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result @@ -72,6 +869,22 @@ where read(buf) } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match this.read(buf) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> @@ -82,3 +895,25 @@ where cursor.advance_checked(n); Ok(()) } + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_buf_exact( + this: &mut R, + mut cursor: BorrowedCursor<'_>, +) -> Result<()> { + while cursor.capacity() > 0 { + let prev_written = cursor.written(); + match this.read_buf(cursor.reborrow()) { + Ok(()) => {} + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + + if cursor.written() == prev_written { + return Err(Error::READ_EXACT_EOF); + } + } + + Ok(()) +} diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs new file mode 100644 index 0000000000000..202b9bf9e3467 --- /dev/null +++ b/library/alloc/src/io/util.rs @@ -0,0 +1,314 @@ +use core::cmp; + +use crate::io::{ + self, BorrowedBuf, BorrowedCursor, Chain, Empty, IoSliceMut, Read, Repeat, Result, SizeHint, + Take, +}; +use crate::slice; +use crate::string::String; +use crate::vec::Vec; + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Empty { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } + + #[inline] + fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + Ok(0) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + // Do not force `Chain` or `Chain` to use vectored + // reads, unless the other reader is vectored. + false + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_to_end(&mut self, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn read_to_string(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Repeat { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + buf.fill(self.byte); + Ok(buf.len()) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + buf.fill(self.byte); + Ok(()) + } + + #[inline] + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + // SAFETY: No uninit bytes are being written. + unsafe { buf.as_mut() }.write_filled(self.byte); + // SAFETY: the entire unfilled portion of buf has been initialized. + unsafe { buf.advance(buf.capacity()) }; + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.read_buf(buf) + } + + /// This function is not supported by `io::Repeat`, because there's no end of its data + fn read_to_end(&mut self, _: &mut Vec) -> io::Result { + Err(io::Error::from(io::ErrorKind::OutOfMemory)) + } + + /// This function is not supported by `io::Repeat`, because there's no end of its data + fn read_to_string(&mut self, _: &mut String) -> io::Result { + Err(io::Error::from(io::ErrorKind::OutOfMemory)) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += self.read(buf)?; + } + Ok(nwritten) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.done_first { + match self.first.read(buf)? { + 0 if !buf.is_empty() => self.done_first = true, + n => return Ok(n), + } + } + self.second.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + if !self.done_first { + match self.first.read_vectored(bufs)? { + 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, + n => return Ok(n), + } + } + self.second.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.first.is_read_vectored() || self.second.is_read_vectored() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let mut read = 0; + if !self.done_first { + read += self.first.read_to_end(buf)?; + self.done_first = true; + } + read += self.second.read_to_end(buf)?; + Ok(read) + } + + // We don't override `read_to_string` here because an UTF-8 sequence could + // be split between the two parts of the chain + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + if buf.capacity() == 0 { + return Ok(()); + } + + if !self.done_first { + let old_len = buf.written(); + self.first.read_buf(buf.reborrow())?; + + if buf.written() != old_len { + return Ok(()); + } else { + self.done_first = true; + } + } + self.second.read_buf(buf) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Take { + fn read(&mut self, buf: &mut [u8]) -> Result { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(0); + } + + let max = cmp::min(buf.len() as u64, self.limit) as usize; + let n = self.inner.read(&mut buf[..max])?; + assert!(n as u64 <= self.limit, "number of read bytes exceeds limit"); + self.limit -= n as u64; + Ok(n) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(()); + } + + if self.limit < buf.capacity() as u64 { + // The condition above guarantees that `self.limit` fits in `usize`. + let limit = self.limit as usize; + + let is_init = buf.is_init(); + + // SAFETY: no uninit data is written to ibuf + let mut sliced_buf = BorrowedBuf::from(unsafe { &mut buf.as_mut()[..limit] }); + + if is_init { + // SAFETY: `sliced_buf` is a subslice of `buf`, so if `buf` was initialized then + // `sliced_buf` is. + unsafe { sliced_buf.set_init() }; + } + + let result = self.inner.read_buf(sliced_buf.unfilled()); + + let did_init_up_to_limit = sliced_buf.is_init(); + let filled = sliced_buf.len(); + + // sliced_buf must drop here + + // Avoid accidentally quadratic behaviour by initializing the whole + // cursor if only part of it was initialized. + if did_init_up_to_limit && !is_init { + // SAFETY: No uninit data will be written. + let unfilled_before_advance = unsafe { buf.as_mut() }; + + unfilled_before_advance[limit..].write_filled(0); + + // SAFETY: `unfilled_before_advance[..limit]` was initialized by `T::read_buf`, and + // `unfilled_before_advance[limit..]` was just initialized. + unsafe { buf.set_init() }; + } + + unsafe { + // SAFETY: filled bytes have been filled + buf.advance(filled); + } + + self.limit -= filled as u64; + + result + } else { + let written = buf.written(); + let result = self.inner.read_buf(buf.reborrow()); + self.limit -= (buf.written() - written) as u64; + result + } + } +} + +/// An iterator over `u8` values of a reader. +/// +/// This struct is generally created by calling [`bytes`] on a reader. +/// Please see the documentation of [`bytes`] for more details. +/// +/// [`bytes`]: Read::bytes +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Bytes { + inner: R, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Bytes { + type Item = Result; + + // Not `#[inline]`. This function gets inlined even without it, but having + // the inline annotation can result in worse code generation. See #116785. + fn next(&mut self) -> Option> { + SpecReadByte::spec_read_byte(&mut self.inner) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + SizeHint::size_hint(&self.inner) + } +} + +/// For the specialization of `Bytes::next`. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait SpecReadByte { + fn spec_read_byte(&mut self) -> Option>; +} + +impl SpecReadByte for R +where + Self: Read, +{ + #[inline] + default fn spec_read_byte(&mut self) -> Option> { + inlined_slow_read_byte(self) + } +} + +/// Reads a single byte in a slow, generic way. This is used by the default +/// `spec_read_byte`. +#[inline] +fn inlined_slow_read_byte(reader: &mut R) -> Option> { + let mut byte = 0; + loop { + return match reader.read(slice::from_mut(&mut byte)) { + Ok(0) => None, + Ok(..) => Some(Ok(byte)), + Err(ref e) if e.is_interrupted() => continue, + Err(e) => Some(Err(e)), + }; + } +} + +// Used by `BufReader::spec_read_byte`, for which the `inline(never)` is +// important. +#[inline(never)] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn uninlined_slow_read_byte(reader: &mut R) -> Option> { + inlined_slow_read_byte(reader) +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const fn bytes(inner: R) -> Bytes { + Bytes { inner } +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 235301d310aaa..7dacec9771997 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -117,6 +117,7 @@ #![feature(core_io)] #![feature(core_io_borrowed_buf)] #![feature(core_io_internals)] +#![feature(cursor_split)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(diagnostic_on_move)] @@ -136,12 +137,15 @@ #![feature(inplace_iteration)] #![feature(io_const_error)] #![feature(io_const_error_internals)] +#![feature(io_slice_as_bytes)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] #![feature(legacy_receiver_trait)] #![feature(likely_unlikely)] #![feature(local_waker)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] @@ -213,6 +217,7 @@ // // Rustdoc features: #![feature(doc_cfg)] +#![feature(doc_notable_trait)] // Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` // blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad // that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 4ff99eb92b98d..eadc634b68273 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -318,6 +318,8 @@ impl BufReader { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SpecReadByte for BufReader where Self: Read, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 36e76b2b68a1b..5c7b7a266d2fc 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -2,90 +2,9 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::Cursor; +pub use alloc::io::Cursor; -use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, IoSliceMut}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Cursor -where - T: AsRef<[u8]>, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = Read::read(&mut Cursor::split(self).1, buf)?; - self.set_position(self.position() + n as u64); - Ok(n) - } - - fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let prev_written = cursor.written(); - - Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; - - self.set_position(self.position() + (cursor.written() - prev_written) as u64); - - Ok(()) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nread = 0; - for buf in bufs { - let n = self.read(buf)?; - nread += n; - if n < buf.len() { - break; - } - } - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - true - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let result = Read::read_exact(&mut Cursor::split(self).1, buf); - - match result { - Ok(_) => self.set_position(self.position() + buf.len() as u64), - // The only possible error condition is EOF, so place the cursor at "EOF" - Err(_) => self.set_position(self.get_ref().as_ref().len() as u64), - } - - result - } - - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let prev_written = cursor.written(); - - let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); - self.set_position(self.position() + (cursor.written() - prev_written) as u64); - - result - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let content = Cursor::split(self).1; - let len = content.len(); - buf.try_reserve(len)?; - buf.extend_from_slice(content); - self.set_position(self.position() + len as u64); - - Ok(len) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - let content = - crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; - let len = content.len(); - buf.try_reserve(len)?; - buf.push_str(content); - self.set_position(self.position() + len as u64); - - Ok(len) - } -} +use crate::io::{self, BufRead}; #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Cursor diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 0939e3a38af1a..7329b56b2fb3f 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,55 +3,11 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSliceMut, Read}; -use crate::sync::Arc; -use crate::{cmp, str}; +use crate::io::{self, BufRead}; // ============================================================================= // Forwarding implementations -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &mut R { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (**self).read_exact(buf) - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf_exact(cursor) - } -} #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] @@ -85,48 +41,6 @@ impl BufRead for &mut B { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Box { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (**self).read_exact(buf) - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf_exact(cursor) - } -} #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] @@ -163,118 +77,6 @@ impl BufRead for Box { // ============================================================================= // In-memory buffer implementations -/// Read is implemented for `&[u8]` by copying from the slice. -/// -/// Note that reading updates the slice to point to the yet unread part. -/// The slice will be empty when EOF is reached. -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &[u8] { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.len()); - let (a, b) = self.split_at(amt); - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if amt == 1 { - buf[0] = a[0]; - } else { - buf[..amt].copy_from_slice(a); - } - - *self = b; - Ok(amt) - } - - #[inline] - fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let amt = cmp::min(cursor.capacity(), self.len()); - let (a, b) = self.split_at(amt); - - cursor.append(a); - - *self = b; - Ok(()) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nread = 0; - for buf in bufs { - nread += self.read(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nread) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if buf.len() > self.len() { - // `read_exact` makes no promise about the content of `buf` if it - // fails so don't bother about that. - *self = &self[self.len()..]; - return Err(io::Error::READ_EXACT_EOF); - } - let (a, b) = self.split_at(buf.len()); - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if buf.len() == 1 { - buf[0] = a[0]; - } else { - buf.copy_from_slice(a); - } - - *self = b; - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - if cursor.capacity() > self.len() { - // Append everything we can to the cursor. - cursor.append(*self); - *self = &self[self.len()..]; - return Err(io::Error::READ_EXACT_EOF); - } - let (a, b) = self.split_at(cursor.capacity()); - - cursor.append(a); - - *self = b; - Ok(()) - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let len = self.len(); - buf.try_reserve(len)?; - buf.extend_from_slice(*self); - *self = &self[len..]; - Ok(len) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; - let len = self.len(); - buf.try_reserve(len)?; - buf.push_str(content); - *self = &self[len..]; - Ok(len) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &[u8] { #[inline] @@ -288,97 +90,6 @@ impl BufRead for &[u8] { } } -/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Read for VecDeque { - /// Fill `buf` with the contents of the "front" slice as returned by - /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are - /// discontiguous, multiple calls to `read` will be needed to read the entire content. - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let (ref mut front, _) = self.as_slices(); - let n = Read::read(front, buf)?; - self.drain(..n); - Ok(n) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let (front, back) = self.as_slices(); - - // Use only the front buffer if it is big enough to fill `buf`, else use - // the back buffer too. - match buf.split_at_mut_checked(front.len()) { - None => buf.copy_from_slice(&front[..buf.len()]), - Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { - Some((back, _)) => { - buf_front.copy_from_slice(front); - buf_back.copy_from_slice(back); - } - None => { - self.clear(); - return Err(io::Error::READ_EXACT_EOF); - } - }, - } - - self.drain(..buf.len()); - Ok(()) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let (ref mut front, _) = self.as_slices(); - let n = cmp::min(cursor.capacity(), front.len()); - Read::read_buf(front, cursor)?; - self.drain(..n); - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let len = cursor.capacity(); - let (front, back) = self.as_slices(); - - match front.split_at_checked(cursor.capacity()) { - Some((front, _)) => cursor.append(front), - None => { - cursor.append(front); - match back.split_at_checked(cursor.capacity()) { - Some((back, _)) => cursor.append(back), - None => { - cursor.append(back); - self.clear(); - return Err(io::Error::READ_EXACT_EOF); - } - } - } - } - - self.drain(..len); - Ok(()) - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - // The total len is known upfront so we can reserve it in a single call. - let len = self.len(); - buf.try_reserve(len)?; - - let (front, back) = self.as_slices(); - buf.extend_from_slice(front); - buf.extend_from_slice(back); - self.clear(); - Ok(len) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - // SAFETY: We only append to the buffer - unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } - } -} - /// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. #[stable(feature = "vecdeque_buf_read", since = "1.75.0")] impl BufRead for VecDeque { @@ -396,50 +107,3 @@ impl BufRead for VecDeque { self.drain(..amt); } } - -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Read for Arc -where - for<'a> &'a R: Read, - R: crate::io::IoHandle, -{ - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (&**self).read(buf) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (&**self).read_buf(cursor) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (&**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (&**self).is_read_vectored() - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (&**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (&**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (&**self).read_exact(buf) - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (&**self).read_buf_exact(cursor) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index f6c2e7c06e80e..0f961420518f4 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,13 +310,15 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, - repeat, sink, + Bytes, Chain, Empty, Error, ErrorKind, Read, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, + empty, repeat, sink, }; #[allow(unused_imports, reason = "only used by certain target configurations")] pub(crate) use alloc_crate::io::{DEFAULT_BUF_SIZE, default_read_buf}; pub(crate) use alloc_crate::io::{ - IoHandle, append_to_string, default_read_vectored, default_write_vectored, stream_len_default, + IoHandle, SpecReadByte, append_to_string, default_read_buf_exact, default_read_exact, + default_read_to_end, default_read_to_string, default_read_vectored, default_write_vectored, + stream_len_default, uninlined_slow_read_byte, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -342,8 +344,7 @@ pub use self::{ cursor::Cursor, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -use crate::mem::MaybeUninit; -use crate::{cmp, slice}; +use crate::cmp; mod buffered; pub(crate) mod copy; @@ -357,809 +358,6 @@ mod util; pub(crate) use stdio::cleanup; -// Here we must serve many masters with conflicting goals: -// -// - avoid allocating unless necessary -// - avoid overallocating if we know the exact size (#89165) -// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) -// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads -// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems -// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) -// -pub(crate) fn default_read_to_end( - r: &mut R, - buf: &mut Vec, - size_hint: Option, -) -> Result { - let start_len = buf.len(); - let start_cap = buf.capacity(); - // Optionally limit the maximum bytes read on each iteration. - // This adds an arbitrary fiddle factor to allow for more data than we expect. - let mut max_read_size = size_hint - .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) - .unwrap_or(DEFAULT_BUF_SIZE); - - const PROBE_SIZE: usize = 32; - - fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { - let mut probe = [0u8; PROBE_SIZE]; - - loop { - match r.read(&mut probe) { - Ok(n) => { - // there is no way to recover from allocation failure here - // because the data has already been read. - buf.extend_from_slice(&probe[..n]); - return Ok(n); - } - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - } - } - - // avoid inflating empty/small vecs before we have determined that there's anything to read - if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(0); - } - } - - loop { - if buf.len() == buf.capacity() && buf.capacity() == start_cap { - // The buffer might be an exact fit. Let's read into a probe buffer - // and see if it returns `Ok(0)`. If so, we've avoided an - // unnecessary doubling of the capacity. But if not, append the - // probe buffer to the primary buffer and let its capacity grow. - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(buf.len() - start_len); - } - } - - if buf.len() == buf.capacity() { - // buf is full, need more space - buf.try_reserve(PROBE_SIZE)?; - } - - let mut spare = buf.spare_capacity_mut(); - let buf_len = cmp::min(spare.len(), max_read_size); - spare = &mut spare[..buf_len]; - let mut read_buf: BorrowedBuf<'_> = spare.into(); - - // Note that we don't track already initialized bytes here, but this is fine - // because we explicitly limit the read size - let mut cursor = read_buf.unfilled(); - let result = loop { - match r.read_buf(cursor.reborrow()) { - Err(e) if e.is_interrupted() => continue, - // Do not stop now in case of error: we might have received both data - // and an error - res => break res, - } - }; - - let bytes_read = cursor.written(); - let is_init = read_buf.is_init(); - - // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. - unsafe { - let new_len = bytes_read + buf.len(); - buf.set_len(new_len); - } - - // Now that all data is pushed to the vector, we can fail without data loss - result?; - - if bytes_read == 0 { - return Ok(buf.len() - start_len); - } - - // Use heuristics to determine the max read size if no initial size hint was provided - if size_hint.is_none() { - // The reader is returning short reads but it doesn't call ensure_init(). - // In that case we no longer need to restrict read sizes to avoid - // initialization costs. - // When reading from disk we usually don't get any short reads except at EOF. - // So we wait for at least 2 short reads before uncapping the read buffer; - // this helps with the Windows issue. - if !is_init { - max_read_size = usize::MAX; - } - // we have passed a larger buffer than previously and the - // reader still hasn't returned a short read - else if buf_len >= max_read_size && bytes_read == buf_len { - max_read_size = max_read_size.saturating_mul(2); - } - } - } -} - -pub(crate) fn default_read_to_string( - r: &mut R, - buf: &mut String, - size_hint: Option, -) -> Result { - // Note that we do *not* call `r.read_to_end()` here. We are passing - // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` - // method to fill it up. An arbitrary implementation could overwrite the - // entire contents of the vector, not just append to it (which is what - // we are expecting). - // - // To prevent extraneously checking the UTF-8-ness of the entire buffer - // we pass it to our hardcoded `default_read_to_end` implementation which - // we know is guaranteed to only read data into the end of the buffer. - unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } -} - -pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { - while !buf.is_empty() { - match this.read(buf) { - Ok(0) => break, - Ok(n) => { - buf = &mut buf[n..]; - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } -} - -pub(crate) fn default_read_buf_exact( - this: &mut R, - mut cursor: BorrowedCursor<'_>, -) -> Result<()> { - while cursor.capacity() > 0 { - let prev_written = cursor.written(); - match this.read_buf(cursor.reborrow()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - - if cursor.written() == prev_written { - return Err(Error::READ_EXACT_EOF); - } - } - - Ok(()) -} - -/// The `Read` trait allows for reading bytes from a source. -/// -/// Implementors of the `Read` trait are called 'readers'. -/// -/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] -/// will attempt to pull bytes from this source into a provided buffer. A -/// number of other methods are implemented in terms of [`read()`], giving -/// implementors a number of ways to read bytes while only needing to implement -/// a single method. -/// -/// Readers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Read` -/// trait. -/// -/// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements [`BufRead`], such as -/// [`BufReader`], will be more efficient. -/// -/// Repeated calls to the reader use the same cursor, so for example -/// calling `read_to_end` twice on a [`File`] will only return the file's -/// contents once. It's recommended to first call `rewind()` in that case. -/// -/// # Examples -/// -/// [`File`]s implement `Read`: -/// -/// ```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 -/// f.read(&mut buffer)?; -/// -/// let mut buffer = Vec::new(); -/// // read the whole file -/// f.read_to_end(&mut buffer)?; -/// -/// // read into a String, so that you don't need to do the conversion. -/// let mut buffer = String::new(); -/// f.read_to_string(&mut buffer)?; -/// -/// // and more! See the other methods for more details. -/// Ok(()) -/// } -/// ``` -/// -/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: -/// -/// ```no_run -/// # use std::io; -/// use std::io::prelude::*; -/// -/// fn main() -> io::Result<()> { -/// let mut b = "This string will be read".as_bytes(); -/// let mut buffer = [0; 10]; -/// -/// // read up to 10 bytes -/// b.read(&mut buffer)?; -/// -/// // etc... it works exactly as a File does! -/// Ok(()) -/// } -/// ``` -/// -/// [`read()`]: Read::read -/// [`&str`]: prim@str -/// [`std::io`]: self -/// [`File`]: crate::fs::File -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] -pub trait Read { - /// Pull some bytes from this source into the specified buffer, returning - /// how many bytes were read. - /// - /// This function does not provide any guarantees about whether it blocks - /// waiting for data, but if an object needs to block for a read and cannot, - /// it will typically signal this via an [`Err`] return value. - /// - /// If the return value of this method is [`Ok(n)`], then implementations must - /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates - /// that the buffer `buf` has been filled in with `n` bytes of data from this - /// source. If `n` is `0`, then it can indicate one of two scenarios: - /// - /// 1. This reader has reached its "end of file" and will likely no longer - /// be able to produce bytes. Note that this does not mean that the - /// reader will *always* no longer be able to produce bytes. As an example, - /// on Linux, this method will call the `recv` syscall for a [`TcpStream`], - /// where returning zero indicates the connection was shut down correctly. While - /// for [`File`], it is possible to reach the end of file and get zero as result, - /// but if more data is appended to the file, future calls to `read` will return - /// more data. - /// 2. The buffer specified was 0 bytes in length. - /// - /// It is not an error if the returned value `n` is smaller than the buffer size, - /// even when the reader is not at the end of the stream yet. - /// This may happen for example because fewer bytes are actually available right now - /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. - /// - /// As this trait is safe to implement, callers in unsafe code cannot rely on - /// `n <= buf.len()` for safety. - /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. - /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if - /// `n > buf.len()`. - /// - /// *Implementations* of this method can make no assumptions about the contents of `buf` when - /// this function is called. It is recommended that implementations only write data to `buf` - /// instead of reading its contents. - /// - /// Correspondingly, however, *callers* of this method in unsafe code must not assume - /// any guarantees about how the implementation uses `buf`. The trait is safe to implement, - /// so it is possible that the code that's supposed to write to the buffer might also read - /// from it. It is your responsibility to make sure that `buf` is initialized - /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one - /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. - /// - /// [`MaybeUninit`]: crate::mem::MaybeUninit - /// - /// # Errors - /// - /// If this function encounters any form of I/O or other error, an error - /// variant will be returned. If an error is returned then it must be - /// guaranteed that no bytes were read. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read - /// operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`Ok(n)`]: Ok - /// [`File`]: crate::fs::File - /// [`TcpStream`]: crate::net::TcpStream - /// - /// ```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(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read(&mut self, buf: &mut [u8]) -> Result; - - /// Like `read`, except that it reads into a slice of buffers. - /// - /// Data is copied to fill each buffer in order, with the final buffer - /// written to possibly being only partially filled. This method must - /// behave equivalently to a single call to `read` with concatenated - /// buffers. - /// - /// The default implementation calls `read` with either the first nonempty - /// buffer provided, or an empty one if none exists. - #[stable(feature = "iovec", since = "1.36.0")] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - default_read_vectored(|b| self.read(b), bufs) - } - - /// Determines if this `Read`er has an efficient `read_vectored` - /// implementation. - /// - /// If a `Read`er does not override the default `read_vectored` - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - #[unstable(feature = "can_vector", issue = "69941")] - fn is_read_vectored(&self) -> bool { - false - } - - /// Reads all bytes until EOF in this source, placing them into `buf`. - /// - /// All bytes read from this source will be appended to the specified buffer - /// `buf`. This function will continuously call [`read()`] to append more data to - /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of - /// non-[`ErrorKind::Interrupted`] kind. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If any other read error is encountered then this function immediately - /// returns. Any bytes which have already been read will be appended to - /// `buf`. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read()`]: Read::read - /// [`Ok(0)`]: Ok - /// [`File`]: crate::fs::File - /// - /// ```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 = Vec::new(); - /// - /// // read the whole file - /// f.read_to_end(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read`] convenience function for reading from a - /// file.) - /// - /// [`std::fs::read`]: crate::fs::read - /// - /// ## Implementing `read_to_end` - /// - /// When implementing the `io::Read` trait, it is recommended to allocate - /// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed - /// by all implementations, and `read_to_end` may not handle out-of-memory - /// situations gracefully. - /// - /// ```no_run - /// # use std::io::{self, BufRead}; - /// # struct Example { example_datasource: io::Empty } impl Example { - /// # fn get_some_data_for_the_example(&self) -> &'static [u8] { &[] } - /// fn read_to_end(&mut self, dest_vec: &mut Vec) -> io::Result { - /// let initial_vec_len = dest_vec.len(); - /// loop { - /// let src_buf = self.example_datasource.fill_buf()?; - /// if src_buf.is_empty() { - /// break; - /// } - /// dest_vec.try_reserve(src_buf.len())?; - /// dest_vec.extend_from_slice(src_buf); - /// - /// // Any irreversible side effects should happen after `try_reserve` succeeds, - /// // to avoid losing data on allocation error. - /// let read = src_buf.len(); - /// self.example_datasource.consume(read); - /// } - /// Ok(dest_vec.len() - initial_vec_len) - /// } - /// # } - /// ``` - /// - /// # Usage Notes - /// - /// `read_to_end` attempts to read a source until EOF, but many sources are continuous streams - /// that do not send EOF. In these cases, `read_to_end` will block indefinitely. Standard input - /// is one such stream which may be finite if piped, but is typically continuous. For example, - /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. - /// Reading user input or running programs that remain open indefinitely will never terminate - /// the stream with `EOF` (e.g. `yes | my-rust-program`). - /// - /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution - /// - ///[`read`]: Read::read - /// - /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - default_read_to_end(self, buf, None) - } - - /// Reads all bytes until EOF in this source, appending them to `buf`. - /// - /// If successful, this function returns the number of bytes which were read - /// and appended to `buf`. - /// - /// # Errors - /// - /// If the data in this stream is *not* valid UTF-8 then an error is - /// returned and `buf` is unchanged. - /// - /// See [`read_to_end`] for other error semantics. - /// - /// [`read_to_end`]: Read::read_to_end - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```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 = String::new(); - /// - /// f.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read_to_string`] convenience function for - /// reading from a file.) - /// - /// # Usage Notes - /// - /// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams - /// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input - /// is one such stream which may be finite if piped, but is typically continuous. For example, - /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. - /// Reading user input or running programs that remain open indefinitely will never terminate - /// the stream with `EOF` (e.g. `yes | my-rust-program`). - /// - /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution - /// - ///[`read`]: Read::read - /// - /// [`std::fs::read_to_string`]: crate::fs::read_to_string - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_string(&mut self, buf: &mut String) -> Result { - default_read_to_string(self, buf, None) - } - - /// Reads the exact number of bytes required to fill `buf`. - /// - /// This function reads as many bytes as necessary to completely fill the - /// specified buffer `buf`. - /// - /// *Implementations* of this method can make no assumptions about the contents of `buf` when - /// this function is called. It is recommended that implementations only write data to `buf` - /// instead of reading its contents. The documentation on [`read`] has a more detailed - /// explanation of this subject. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read`]: Read::read - /// [`File`]: crate::fs::File - /// - /// ```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 exactly 10 bytes - /// f.read_exact(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "read_exact", since = "1.6.0")] - fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { - default_read_exact(self, buf) - } - - /// Pull some bytes from this source into the specified buffer. - /// - /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use - /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`. - /// - /// The default implementation delegates to `read`. - /// - /// This method makes it possible to return both data and an error but it is advised against. - #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - default_read_buf(|b| self.read(b), buf) - } - - /// Reads the exact number of bytes required to fill `cursor`. - /// - /// This is similar to the [`read_exact`](Read::read_exact) method, except - /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use - /// with uninitialized buffers. - /// - /// # Errors - /// - /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] - /// then the error is ignored and the operation will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// - /// If any other read error is encountered then this function immediately - /// returns. - /// - /// If this function returns an error, all bytes read will be appended to `cursor`. - #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { - default_read_buf_exact(self, cursor) - } - - /// Creates a "by reference" adapter for this instance of `Read`. - /// - /// The returned adapter also implements `Read` and will simply borrow this - /// current reader. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::Read; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = Vec::new(); - /// let mut other_buffer = Vec::new(); - /// - /// { - /// let reference = f.by_ref(); - /// - /// // read at most 5 bytes - /// reference.take(5).read_to_end(&mut buffer)?; - /// - /// } // drop our &mut reference so we can use f again - /// - /// // original file still usable, read the rest - /// f.read_to_end(&mut other_buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } - - /// Transforms this `Read` instance to an [`Iterator`] over its bytes. - /// - /// The returned type implements [`Iterator`] where the [`Item`] is - /// [Result]<[u8], [io::Error]>. - /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] - /// otherwise. EOF is mapped to returning [`None`] from this iterator. - /// - /// The default implementation calls `read` for each byte, - /// which can be very inefficient for data that's not in memory, - /// such as [`File`]. Consider using a [`BufReader`] in such cases. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`Item`]: Iterator::Item - /// [`File`]: crate::fs::File "fs::File" - /// [Result]: crate::result::Result "Result" - /// [io::Error]: self::Error "io::Error" - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = BufReader::new(File::open("foo.txt")?); - /// - /// for byte in f.bytes() { - /// println!("{}", byte?); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn bytes(self) -> Bytes - where - Self: Sized, - { - Bytes { inner: self } - } - - /// Creates an adapter which will chain this stream with another. - /// - /// The returned `Read` instance will first read all bytes from this object - /// until EOF is encountered. Afterwards the output is equivalent to the - /// output of `next`. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f1 = File::open("foo.txt")?; - /// let f2 = File::open("bar.txt")?; - /// - /// let mut handle = f1.chain(f2); - /// let mut buffer = String::new(); - /// - /// // read the value into a String. We could use any Read method here, - /// // this is just one example. - /// handle.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn chain(self, next: R) -> Chain - where - Self: Sized, - { - core::io::chain(self, next) - } - - /// Creates an adapter which will read at most `limit` bytes from it. - /// - /// This function returns a new instance of `Read` which will read at most - /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any - /// read errors will not count towards the number of bytes read and future - /// calls to [`read()`] may succeed. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// [`Ok(0)`]: Ok - /// [`read()`]: Read::read - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// let mut buffer = [0; 5]; - /// - /// // read at most five bytes - /// let mut handle = f.take(5); - /// - /// handle.read(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn take(self, limit: u64) -> Take - where - Self: Sized, - { - core::io::take(self, limit) - } - - /// Read and return a fixed array of bytes from this source. - /// - /// This function uses an array sized based on a const generic size known at compile time. You - /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference - /// determine the number of bytes needed based on how the return value gets used. For instance, - /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of - /// bytes into an integer of the same size. - /// - /// Like `read_exact`, if this function encounters an "end of file" before reading the desired - /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// - /// ``` - /// #![feature(read_array)] - /// use std::io::Cursor; - /// use std::io::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); - /// let x = u64::from_le_bytes(buf.read_array()?); - /// let y = u32::from_be_bytes(buf.read_array()?); - /// let z = u16::from_be_bytes(buf.read_array()?); - /// assert_eq!(x, 0x807060504030201); - /// assert_eq!(y, 0x9080706); - /// assert_eq!(z, 0x504); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "read_array", issue = "148848")] - fn read_array(&mut self) -> Result<[u8; N]> - where - Self: Sized, - { - let mut buf = [MaybeUninit::uninit(); N]; - let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); - self.read_buf_exact(borrowed_buf.unfilled())?; - // Guard against incorrect `read_buf_exact` implementations. - assert_eq!(borrowed_buf.len(), N); - Ok(unsafe { MaybeUninit::array_assume_init(buf) }) - } -} - /// Reads all bytes from a [reader][Read] into a new [`String`]. /// /// This is a convenience function for [`Read::read_to_string`]. Using this @@ -1688,65 +886,6 @@ pub trait BufRead: Read { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Chain { - fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.done_first { - match self.first.read(buf)? { - 0 if !buf.is_empty() => self.done_first = true, - n => return Ok(n), - } - } - self.second.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - if !self.done_first { - match self.first.read_vectored(bufs)? { - 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, - n => return Ok(n), - } - } - self.second.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.first.is_read_vectored() || self.second.is_read_vectored() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - let mut read = 0; - if !self.done_first { - read += self.first.read_to_end(buf)?; - self.done_first = true; - } - read += self.second.read_to_end(buf)?; - Ok(read) - } - - // We don't override `read_to_string` here because an UTF-8 sequence could - // be split between the two parts of the chain - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { - if buf.capacity() == 0 { - return Ok(()); - } - - if !self.done_first { - let old_len = buf.written(); - self.first.read_buf(buf.reborrow())?; - - if buf.written() != old_len { - return Ok(()); - } else { - self.done_first = true; - } - } - self.second.read_buf(buf) - } -} - #[stable(feature = "chain_bufread", since = "1.9.0")] impl BufRead for Chain { fn fill_buf(&mut self) -> Result<&[u8]> { @@ -1782,79 +921,6 @@ impl BufRead for Chain { // split between the two parts of the chain } -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Take { - fn read(&mut self, buf: &mut [u8]) -> Result { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(0); - } - - let max = cmp::min(buf.len() as u64, self.limit) as usize; - let n = self.inner.read(&mut buf[..max])?; - assert!(n as u64 <= self.limit, "number of read bytes exceeds limit"); - self.limit -= n as u64; - Ok(n) - } - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(()); - } - - if self.limit < buf.capacity() as u64 { - // The condition above guarantees that `self.limit` fits in `usize`. - let limit = self.limit as usize; - - let is_init = buf.is_init(); - - // SAFETY: no uninit data is written to ibuf - let mut sliced_buf = BorrowedBuf::from(unsafe { &mut buf.as_mut()[..limit] }); - - if is_init { - // SAFETY: `sliced_buf` is a subslice of `buf`, so if `buf` was initialized then - // `sliced_buf` is. - unsafe { sliced_buf.set_init() }; - } - - let result = self.inner.read_buf(sliced_buf.unfilled()); - - let did_init_up_to_limit = sliced_buf.is_init(); - let filled = sliced_buf.len(); - - // sliced_buf must drop here - - // Avoid accidentally quadratic behaviour by initializing the whole - // cursor if only part of it was initialized. - if did_init_up_to_limit && !is_init { - // SAFETY: No uninit data will be written. - let unfilled_before_advance = unsafe { buf.as_mut() }; - - unfilled_before_advance[limit..].write_filled(0); - - // SAFETY: `unfilled_before_advance[..limit]` was initialized by `T::read_buf`, and - // `unfilled_before_advance[limit..]` was just initialized. - unsafe { buf.set_init() }; - } - - unsafe { - // SAFETY: filled bytes have been filled - buf.advance(filled); - } - - self.limit -= filled as u64; - - result - } else { - let written = buf.written(); - let result = self.inner.read_buf(buf.reborrow()); - self.limit -= (buf.written() - written) as u64; - result - } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Take { fn fill_buf(&mut self) -> Result<&[u8]> { @@ -1876,71 +942,6 @@ impl BufRead for Take { } } -/// An iterator over `u8` values of a reader. -/// -/// This struct is generally created by calling [`bytes`] on a reader. -/// Please see the documentation of [`bytes`] for more details. -/// -/// [`bytes`]: Read::bytes -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Bytes { - inner: R, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes { - type Item = Result; - - // Not `#[inline]`. This function gets inlined even without it, but having - // the inline annotation can result in worse code generation. See #116785. - fn next(&mut self) -> Option> { - SpecReadByte::spec_read_byte(&mut self.inner) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - SizeHint::size_hint(&self.inner) - } -} - -/// For the specialization of `Bytes::next`. -trait SpecReadByte { - fn spec_read_byte(&mut self) -> Option>; -} - -impl SpecReadByte for R -where - Self: Read, -{ - #[inline] - default fn spec_read_byte(&mut self) -> Option> { - inlined_slow_read_byte(self) - } -} - -/// Reads a single byte in a slow, generic way. This is used by the default -/// `spec_read_byte`. -#[inline] -fn inlined_slow_read_byte(reader: &mut R) -> Option> { - let mut byte = 0; - loop { - return match reader.read(slice::from_mut(&mut byte)) { - Ok(0) => None, - Ok(..) => Some(Ok(byte)), - Err(ref e) if e.is_interrupted() => continue, - Err(e) => Some(Err(e)), - }; - } -} - -// Used by `BufReader::spec_read_byte`, for which the `inline(never)` is -// important. -#[inline(never)] -fn uninlined_slow_read_byte(reader: &mut R) -> Option> { - inlined_slow_read_byte(reader) -} - /// An iterator over the contents of an instance of `BufRead` split on a /// particular byte. /// diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 5498ccd11b26b..e2ea823b8520b 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -544,6 +544,8 @@ impl Read for StdinLock<'_> { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SpecReadByte for StdinLock<'_> { #[inline] fn spec_read_byte(&mut self) -> Option> { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index f213f4ed7313b..936727f5fbc62 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -3,52 +3,8 @@ #[cfg(test)] mod tests; -use crate::io::{self, BorrowedCursor, BufRead, Empty, IoSliceMut, Read, Repeat}; +use crate::io::{self, BufRead, Empty}; -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Empty { - #[inline] - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } - - #[inline] - fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - // Do not force `Chain` or `Chain` to use vectored - // reads, unless the other reader is vectored. - false - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } - } - - #[inline] - fn read_to_end(&mut self, _buf: &mut Vec) -> io::Result { - Ok(0) - } - - #[inline] - fn read_to_string(&mut self, _buf: &mut String) -> io::Result { - Ok(0) - } -} #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Empty { #[inline] @@ -79,56 +35,3 @@ impl BufRead for Empty { Ok(0) } } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Repeat { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - buf.fill(self.byte); - Ok(buf.len()) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - buf.fill(self.byte); - Ok(()) - } - - #[inline] - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - // SAFETY: No uninit bytes are being written. - unsafe { buf.as_mut() }.write_filled(self.byte); - // SAFETY: the entire unfilled portion of buf has been initialized. - unsafe { buf.advance(buf.capacity()) }; - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.read_buf(buf) - } - - /// This function is not supported by `io::Repeat`, because there's no end of its data - fn read_to_end(&mut self, _: &mut Vec) -> io::Result { - Err(io::Error::from(io::ErrorKind::OutOfMemory)) - } - - /// This function is not supported by `io::Repeat`, because there's no end of its data - fn read_to_string(&mut self, _: &mut String) -> io::Result { - Err(io::Error::from(io::ErrorKind::OutOfMemory)) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.read(buf)?; - } - Ok(nwritten) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index de1eed5fb2e0a..a3921dd994d30 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -396,6 +396,7 @@ #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] +#![feature(read_buf)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] diff --git a/tests/rustdoc-html/jump-to-def/non-local-method.rs b/tests/rustdoc-html/jump-to-def/non-local-method.rs index e084899b2495e..e785ab8d204f9 100644 --- a/tests/rustdoc-html/jump-to-def/non-local-method.rs +++ b/tests/rustdoc-html/jump-to-def/non-local-method.rs @@ -6,7 +6,7 @@ //@ has - '//a[@href="{{channel}}/alloc/boxed/struct.Box.html"]' 'std::boxed::Box' use std::boxed::Box; -//@ has - '//a[@href="{{channel}}/std/io/trait.Read.html"]' 'std::io::Read' +//@ has - '//a[@href="{{channel}}/alloc/io/read/trait.Read.html"]' 'std::io::Read' use std::io::Read; //@ has - '//a[@href="{{channel}}/std/io/index.html"]' 'std::io' use std::io; @@ -16,7 +16,7 @@ use std::cmp::Ordering; use std::marker::PhantomData; pub fn bar2(readable: T) { - //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read' + //@ has - '//a[@href="{{channel}}/alloc/io/read/trait.Read.html#tymethod.read"]' 'read' let _ = readable.read(&mut []); } From aeee071b3048ee991c68489938ea3e962d30652f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 13:24:27 +1000 Subject: [PATCH 29/43] Move `std::io::read_to_string` to `alloc::io` --- library/alloc/src/io/mod.rs | 7 ++-- library/alloc/src/io/read.rs | 65 ++++++++++++++++++++++++++++++++++ library/std/src/io/mod.rs | 67 ++---------------------------------- 3 files changed, 72 insertions(+), 67 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 233abb49137c8..0cc464dbbcf1a 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -27,8 +27,6 @@ pub use core::io::{ slice_write_vectored, stream_len_default, take, }; -#[unstable(feature = "alloc_io", issue = "154046")] -pub use self::{read::Read, util::Bytes}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ @@ -38,3 +36,8 @@ pub use self::{ }, util::{SpecReadByte, bytes, uninlined_slow_read_byte}, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{ + read::{Read, read_to_string}, + util::Bytes, +}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 046ef97c06550..da3c2cf17413a 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -630,6 +630,71 @@ pub trait Read { } } +/// Reads all bytes from a [reader][Read] into a new [`String`]. +/// +/// This is a convenience function for [`Read::read_to_string`]. Using this +/// function avoids having to create a variable first and provides more type +/// safety since you can only get the buffer out if there were no errors. (If you +/// use [`Read::read_to_string`] you have to remember to check whether the read +/// succeeded because otherwise your buffer will be empty or only partially full.) +/// +/// # Performance +/// +/// The downside of this function's increased ease of use and type safety is +/// that it gives you less control over performance. For example, you can't +/// pre-allocate memory like you can using [`String::with_capacity`] and +/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error +/// occurs while reading. +/// +/// In many cases, this function's performance will be adequate and the ease of use +/// and type safety tradeoffs will be worth it. However, there are cases where you +/// need more control over performance, and in those cases you should definitely use +/// [`Read::read_to_string`] directly. +/// +/// Note that in some special cases, such as when reading files, this function will +/// pre-allocate memory based on the size of the input it is reading. In those +/// cases, the performance should be as good as if you had used +/// [`Read::read_to_string`] with a manually pre-allocated buffer. +/// +/// # Errors +/// +/// This function forces you to handle errors because the output (the `String`) +/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors +/// that can occur. If any error occurs, you will get an [`Err`], so you +/// don't have to worry about your buffer being empty or partially full. +/// +/// # Examples +/// +/// ```no_run +/// # use std::io; +/// fn main() -> io::Result<()> { +/// let stdin = io::read_to_string(io::stdin())?; +/// println!("Stdin was:"); +/// println!("{stdin}"); +/// Ok(()) +/// } +/// ``` +/// +/// # Usage Notes +/// +/// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams +/// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input +/// is one such stream which may be finite if piped, but is typically continuous. For example, +/// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. +/// Reading user input or running programs that remain open indefinitely will never terminate +/// the stream with `EOF` (e.g. `yes | my-rust-program`). +/// +/// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution +/// +/// [`read`]: Read::read +/// +#[stable(feature = "io_read_to_string", since = "1.65.0")] +pub fn read_to_string(mut reader: R) -> Result { + let mut buf = String::new(); + reader.read_to_string(&mut buf)?; + Ok(buf) +} + // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) #[doc(hidden)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 0f961420518f4..2a7467a2bab22 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,6 +306,8 @@ pub use alloc_crate::io::RawOsError; pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use alloc_crate::io::const_error; +#[stable(feature = "io_read_to_string", since = "1.65.0")] +pub use alloc_crate::io::read_to_string; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] @@ -358,71 +360,6 @@ mod util; pub(crate) use stdio::cleanup; -/// Reads all bytes from a [reader][Read] into a new [`String`]. -/// -/// This is a convenience function for [`Read::read_to_string`]. Using this -/// function avoids having to create a variable first and provides more type -/// safety since you can only get the buffer out if there were no errors. (If you -/// use [`Read::read_to_string`] you have to remember to check whether the read -/// succeeded because otherwise your buffer will be empty or only partially full.) -/// -/// # Performance -/// -/// The downside of this function's increased ease of use and type safety is -/// that it gives you less control over performance. For example, you can't -/// pre-allocate memory like you can using [`String::with_capacity`] and -/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error -/// occurs while reading. -/// -/// In many cases, this function's performance will be adequate and the ease of use -/// and type safety tradeoffs will be worth it. However, there are cases where you -/// need more control over performance, and in those cases you should definitely use -/// [`Read::read_to_string`] directly. -/// -/// Note that in some special cases, such as when reading files, this function will -/// pre-allocate memory based on the size of the input it is reading. In those -/// cases, the performance should be as good as if you had used -/// [`Read::read_to_string`] with a manually pre-allocated buffer. -/// -/// # Errors -/// -/// This function forces you to handle errors because the output (the `String`) -/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors -/// that can occur. If any error occurs, you will get an [`Err`], so you -/// don't have to worry about your buffer being empty or partially full. -/// -/// # Examples -/// -/// ```no_run -/// # use std::io; -/// fn main() -> io::Result<()> { -/// let stdin = io::read_to_string(io::stdin())?; -/// println!("Stdin was:"); -/// println!("{stdin}"); -/// Ok(()) -/// } -/// ``` -/// -/// # Usage Notes -/// -/// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams -/// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input -/// is one such stream which may be finite if piped, but is typically continuous. For example, -/// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. -/// Reading user input or running programs that remain open indefinitely will never terminate -/// the stream with `EOF` (e.g. `yes | my-rust-program`). -/// -/// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution -/// -///[`read`]: Read::read -/// -#[stable(feature = "io_read_to_string", since = "1.65.0")] -pub fn read_to_string(mut reader: R) -> Result { - let mut buf = String::new(); - reader.read_to_string(&mut buf)?; - Ok(buf) -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { From 3473829212f1301cf2cc77b66ea16abfd9e67573 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:09:23 +1000 Subject: [PATCH 30/43] Move `std::io::BufRead` to `alloc::io` --- library/alloc/src/io/buf_read.rs | 496 +++++++++++++++ library/alloc/src/io/cursor.rs | 18 +- library/alloc/src/io/impls.rs | 101 ++- library/alloc/src/io/mod.rs | 14 +- library/alloc/src/io/util.rs | 172 ++++- library/std/src/io/cursor.rs | 18 - library/std/src/io/impls.rs | 107 ---- library/std/src/io/mod.rs | 596 +----------------- library/std/src/io/util.rs | 35 - library/std/src/lib.rs | 1 + .../where.SWhere_TraitWhere_item-decl.html | 2 +- 11 files changed, 794 insertions(+), 766 deletions(-) create mode 100644 library/alloc/src/io/buf_read.rs diff --git a/library/alloc/src/io/buf_read.rs b/library/alloc/src/io/buf_read.rs new file mode 100644 index 0000000000000..c4467fcb495e3 --- /dev/null +++ b/library/alloc/src/io/buf_read.rs @@ -0,0 +1,496 @@ +use core::slice::memchr; + +use crate::io::{ErrorKind, Lines, Read, Result, Split, append_to_string, lines, split}; +use crate::string::String; +use crate::vec::Vec; + +/// A `BufRead` is a type of [`Read`]er which has an internal buffer, allowing it +/// to perform extra ways of reading. +/// +/// For example, reading line-by-line is inefficient without using a buffer, so +/// if you want to read by line, you'll need `BufRead`, which includes a +/// [`read_line`] method as well as a [`lines`] iterator. +/// +/// # Examples +/// +/// A locked standard input implements `BufRead`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// +/// let stdin = io::stdin(); +/// for line in stdin.lock().lines() { +/// println!("{}", line?); +/// } +/// # std::io::Result::Ok(()) +/// ``` +/// +/// If you have something that implements [`Read`], you can use the `BufReader` +/// type to turn it into a `BufRead`. +/// +/// For example, `File` implements [`Read`], but not `BufRead`. +/// `BufReader` to the rescue! +/// +/// [`read_line`]: BufRead::read_line +/// [`lines`]: BufRead::lines +/// +/// ```no_run +/// use std::io::{self, BufReader}; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let f = File::open("foo.txt")?; +/// let f = BufReader::new(f); +/// +/// for line in f.lines() { +/// let line = line?; +/// println!("{line}"); +/// } +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] +pub trait BufRead: Read { + /// Returns the contents of the internal buffer, filling it with more data, via [`Read`] methods, if empty. + /// + /// This is a lower-level method and is meant to be used together with [`consume`], + /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. + /// + /// [`consume`]: BufRead::consume + /// + /// Returns an empty buffer when the stream has reached EOF. + /// + /// # Errors + /// + /// This function will return an I/O error if a [`Read`] method was called, but returned an error. + /// + /// # Examples + /// + /// A locked standard input implements `BufRead`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// let buffer = stdin.fill_buf()?; + /// + /// // work with buffer + /// println!("{buffer:?}"); + /// + /// // mark the bytes we worked with as read + /// let length = buffer.len(); + /// stdin.consume(length); + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Marks the given `amount` of additional bytes from the internal buffer as having been read. + /// Subsequent calls to `read` only return bytes that have not been marked as read. + /// + /// This is a lower-level method and is meant to be used together with [`fill_buf`], + /// which can be used to fill the internal buffer via [`Read`] methods. + /// + /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. + /// + /// # Examples + /// + /// Since `consume()` is meant to be used with [`fill_buf`], + /// that method's example includes an example of `consume()`. + /// + /// [`fill_buf`]: BufRead::fill_buf + #[stable(feature = "rust1", since = "1.0.0")] + fn consume(&mut self, amount: usize); + + /// Checks if there is any data left to be `read`. + /// + /// This function may fill the buffer to check for data, + /// so this function returns `Result`, not `bool`. + /// + /// The default implementation calls `fill_buf` and checks that the + /// returned slice is empty (which means that there is no data left, + /// since EOF is reached). + /// + /// # Errors + /// + /// This function will return an I/O error if a [`Read`] method was called, but returned an error. + /// + /// Examples + /// + /// ``` + /// #![feature(buf_read_has_data_left)] + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// while stdin.has_data_left()? { + /// let mut line = String::new(); + /// stdin.read_line(&mut line)?; + /// // work with line + /// println!("{line:?}"); + /// } + /// # std::io::Result::Ok(()) + /// ``` + #[unstable(feature = "buf_read_has_data_left", issue = "86423")] + fn has_data_left(&mut self) -> Result { + self.fill_buf().map(|b| !b.is_empty()) + } + + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. + /// + /// This function will read bytes from the underlying stream until the + /// delimiter or EOF is found. Once found, all bytes up to, and including, + /// the delimiter (if found) will be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the bytes in a byte slice + /// in hyphen delimited segments: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); + /// let mut buf = vec![]; + /// + /// // cursor is at 'l' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 6); + /// assert_eq!(buf, b"lorem-"); + /// buf.clear(); + /// + /// // cursor is at 'i' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 5); + /// assert_eq!(buf, b"ipsum"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, b""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + read_until(self, byte, buf) + } + + /// Skips all bytes until the delimiter `byte` or EOF is reached. + /// + /// This function will read (and discard) bytes from the underlying stream until the + /// delimiter or EOF is found. + /// + /// If successful, this function will return the total number of bytes read, + /// including the delimiter byte if found. + /// + /// This is useful for efficiently skipping data such as NUL-terminated strings + /// in binary file formats without buffering. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read some NUL-terminated information + /// about Ferris from a binary string, skipping the fun fact: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); + /// + /// // read name + /// let mut name = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut name) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 7); + /// assert_eq!(name, b"Ferris\0"); + /// + /// // skip fun fact + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 30); + /// + /// // read animal type + /// let mut animal = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut animal) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 11); + /// assert_eq!(animal, b"Crustacean\0"); + /// + /// // reach EOF + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 1); + /// ``` + #[stable(feature = "bufread_skip_until", since = "1.83.0")] + fn skip_until(&mut self, byte: u8) -> Result { + skip_until(self, byte) + } + + /// Reads all bytes until a newline (the `0xA` byte) is reached, and append + /// them to the provided [`String`] buffer. + /// + /// Previous content of the buffer will be preserved. To avoid appending to + /// the buffer, you need to [`clear`] it first. + /// + /// This function will read bytes from the underlying stream until the + /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes + /// up to, and including, the delimiter (if found) will be appended to + /// `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// If this function returns [`Ok(0)`], the stream has reached EOF. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending a newline + /// or EOF. You can use [`take`] to limit the maximum number of bytes read. + /// + /// [`Ok(0)`]: Ok + /// [`clear`]: String::clear + /// [`take`]: crate::io::Read::take + /// + /// # Errors + /// + /// This function has the same error semantics as [`read_until`] and will + /// also return an error if the read bytes are not valid UTF-8. If an I/O + /// error is encountered then `buf` may contain some bytes already read in + /// the event that all data read so far was valid UTF-8. + /// + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the lines in a byte slice: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"foo\nbar"); + /// let mut buf = String::new(); + /// + /// // cursor is at 'f' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 4); + /// assert_eq!(buf, "foo\n"); + /// buf.clear(); + /// + /// // cursor is at 'b' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 3); + /// assert_eq!(buf, "bar"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, ""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_line(&mut self, buf: &mut String) -> Result { + // Note that we are not calling the `.read_until` method here, but + // rather our hardcoded implementation. For more details as to why, see + // the comments in `default_read_to_string`. + unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } + } + + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + /// + /// The iterator returned from this function will return instances of + /// [io::Result]<[Vec]\>. Each vector returned will *not* have + /// the delimiter byte at the end. + /// + /// This function will yield errors whenever [`read_until`] would have + /// also yielded an error. + /// + /// [io::Result]: self::Result "io::Result" + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all hyphen delimited + /// segments in a byte slice + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + /// + /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); + /// assert_eq!(split_iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + split(self, byte) + } + + /// Returns an iterator over the lines of this reader. + /// + /// The iterator returned from this function will yield instances of + /// [io::Result]<[String]>. Each string returned will *not* have a newline + /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. + /// + /// [io::Result]: crate::io::Result "io::Result" + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all the lines in a byte + /// slice. + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); + /// + /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); + /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); + /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); + /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); + /// assert_eq!(lines_iter.next(), None); + /// ``` + /// + /// # Errors + /// + /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. + #[stable(feature = "rust1", since = "1.0.0")] + fn lines(self) -> Lines + where + Self: Sized, + { + lines(self) + } +} + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + }; + let (done, available) = match memchr::memchr(delim, available) { + Some(i) => (true, &available[..=i]), + None => (false, available), + }; + + cfg_select! { + no_global_oom_handling => { + let count = available.len(); + buf.try_reserve(count)?; + + // SAFETY: + // * self and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + unsafe { + let len = buf.len(); + let src = available.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.extend_from_slice(available); + } + } + + (done, available.len()) + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} + +fn skip_until(r: &mut R, delim: u8) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => (true, i + 1), + None => (false, available.len()), + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index a0cafe96d5f04..96235a83e8a03 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -1,8 +1,9 @@ use crate::alloc::Allocator; use crate::boxed::Box; use crate::io::{ - self, BorrowedCursor, Cursor, ErrorKind, IoSlice, IoSliceMut, Read, WriteThroughCursor, - slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, + self, BorrowedCursor, BufRead, Cursor, ErrorKind, IoSlice, IoSliceMut, Read, + WriteThroughCursor, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, }; use crate::string::String; use crate::vec::Vec; @@ -130,6 +131,19 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Cursor +where + T: AsRef<[u8]>, +{ + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(Cursor::split(self).1) + } + fn consume(&mut self, amt: usize) { + self.set_position(self.position() + amt as u64); + } +} + /// Reserves the required space, and pads the vec with 0s if necessary. fn reserve_and_pad( pos_mut: &mut u64, diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index be2e2bc9b27cc..272f91f731d03 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -5,7 +5,9 @@ use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::collections::VecDeque; use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; +use crate::io::{ + self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, +}; use crate::string::String; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; @@ -56,6 +58,38 @@ impl Read for &mut R { (**self).read_buf_exact(cursor) } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for &mut B { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Read for Box { @@ -177,6 +211,38 @@ impl Seek for Box { (**self).seek_relative(offset) } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Box { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} // ============================================================================= // In-memory buffer implementations @@ -338,6 +404,19 @@ impl Read for &[u8] { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} + /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] @@ -499,6 +578,26 @@ impl Read for VecDeque { unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } } } + +/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] +impl BufRead for VecDeque { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} + /// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. #[cfg(not(no_global_oom_handling))] #[stable(feature = "vecdeque_read_write", since = "1.63.0")] diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 0cc464dbbcf1a..96f4ca0a8c4d5 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,6 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod buf_read; mod cursor; mod error; mod impls; @@ -27,6 +28,12 @@ pub use core::io::{ slice_write_vectored, stream_len_default, take, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{ + buf_read::BufRead, + 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::{ @@ -34,10 +41,5 @@ pub use self::{ DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, default_read_exact, default_read_to_end, default_read_to_string, default_read_vectored, }, - util::{SpecReadByte, bytes, uninlined_slow_read_byte}, -}; -#[unstable(feature = "alloc_io", issue = "154046")] -pub use self::{ - read::{Read, read_to_string}, - util::Bytes, + util::{SpecReadByte, bytes, lines, split, uninlined_slow_read_byte}, }; diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs index 202b9bf9e3467..5c2245043e066 100644 --- a/library/alloc/src/io/util.rs +++ b/library/alloc/src/io/util.rs @@ -1,8 +1,8 @@ use core::cmp; use crate::io::{ - self, BorrowedBuf, BorrowedCursor, Chain, Empty, IoSliceMut, Read, Repeat, Result, SizeHint, - Take, + self, BorrowedBuf, BorrowedCursor, BufRead, Chain, Empty, IoSliceMut, Read, Repeat, Result, + SizeHint, Take, }; use crate::slice; use crate::string::String; @@ -53,6 +53,37 @@ impl Read for Empty { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Empty { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(&[]) + } + + #[inline] + fn consume(&mut self, _n: usize) {} + + #[inline] + fn has_data_left(&mut self) -> io::Result { + Ok(false) + } + + #[inline] + fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn skip_until(&mut self, _byte: u8) -> io::Result { + Ok(0) + } + + #[inline] + fn read_line(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { #[inline] @@ -165,6 +196,41 @@ impl Read for Chain { } } +#[stable(feature = "chain_bufread", since = "1.9.0")] +impl BufRead for Chain { + fn fill_buf(&mut self) -> Result<&[u8]> { + if !self.done_first { + match self.first.fill_buf()? { + buf if buf.is_empty() => self.done_first = true, + buf => return Ok(buf), + } + } + self.second.fill_buf() + } + + fn consume(&mut self, amt: usize) { + if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } + } + + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + let mut read = 0; + if !self.done_first { + let n = self.first.read_until(byte, buf)?; + read += n; + + match buf.last() { + Some(b) if *b == byte && n != 0 => return Ok(read), + _ => self.done_first = true, + } + } + read += self.second.read_until(byte, buf)?; + Ok(read) + } + + // We don't override `read_line` here because an UTF-8 sequence could be + // split between the two parts of the chain +} + #[stable(feature = "rust1", since = "1.0.0")] impl Read for Take { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -238,6 +304,27 @@ impl Read for Take { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Take { + fn fill_buf(&mut self) -> Result<&[u8]> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(&[]); + } + + let buf = self.inner.fill_buf()?; + let cap = cmp::min(buf.len() as u64, self.limit) as usize; + Ok(&buf[..cap]) + } + + fn consume(&mut self, amt: usize) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt as u64, self.limit) as usize; + self.limit -= amt as u64; + self.inner.consume(amt); + } +} + /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. @@ -312,3 +399,84 @@ pub fn uninlined_slow_read_byte(reader: &mut R) -> Option> { pub const fn bytes(inner: R) -> Bytes { Bytes { inner } } + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// This struct is generally created by calling [`split`] on a `BufRead`. +/// Please see the documentation of [`split`] for more details. +/// +/// [`split`]: BufRead::split +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSplit")] +pub struct Split { + buf: B, + delim: u8, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const fn split(buf: B, delim: u8) -> Split { + Split { buf, delim } +} + +/// An iterator over the lines of an instance of [`BufRead`]. +/// +/// This struct is generally created by calling [`lines`] on a [`BufRead`]. +/// Please see the documentation of [`lines`] for more details. +/// +/// [`lines`]: BufRead::lines +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] +pub struct Lines { + buf: B, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const fn lines(buf: B) -> Lines { + Lines { buf } +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5c7b7a266d2fc..87c2771955a9d 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -1,20 +1,2 @@ #[cfg(test)] mod tests; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc::io::Cursor; - -use crate::io::{self, BufRead}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Cursor -where - T: AsRef<[u8]>, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(Cursor::split(self).1) - } - fn consume(&mut self, amt: usize) { - self.set_position(self.position() + amt as u64); - } -} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 7329b56b2fb3f..87c2771955a9d 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -1,109 +1,2 @@ #[cfg(test)] mod tests; - -use crate::alloc::Allocator; -use crate::collections::VecDeque; -use crate::io::{self, BufRead}; - -// ============================================================================= -// Forwarding implementations - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &mut B { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Box { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -// ============================================================================= -// In-memory buffer implementations - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &[u8] { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(*self) - } - - #[inline] - fn consume(&mut self, amt: usize) { - *self = &self[amt..]; - } -} - -/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] -impl BufRead for VecDeque { - /// Returns the contents of the "front" slice as returned by - /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are - /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let (front, _) = self.as_slices(); - Ok(front) - } - - #[inline] - fn consume(&mut self, amt: usize) { - self.drain(..amt); - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 2a7467a2bab22..b13eb22382fac 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,8 +297,6 @@ #[cfg(test)] mod tests; -use core::slice::memchr; - #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -312,8 +310,8 @@ pub use alloc_crate::io::read_to_string; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Bytes, Chain, Empty, Error, ErrorKind, Read, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, - empty, repeat, sink, + BufRead, Bytes, Chain, Cursor, Empty, Error, ErrorKind, Lines, Read, Repeat, Result, Seek, + SeekFrom, Sink, Split, Take, Write, empty, repeat, sink, }; #[allow(unused_imports, reason = "only used by certain target configurations")] pub(crate) use alloc_crate::io::{DEFAULT_BUF_SIZE, default_read_buf}; @@ -343,10 +341,8 @@ pub use self::stdio::{set_output_capture, try_set_output_capture}; pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, - cursor::Cursor, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -use crate::cmp; mod buffered; pub(crate) mod copy; @@ -359,591 +355,3 @@ mod stdio; mod util; pub(crate) use stdio::cleanup; - -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => { - buf.extend_from_slice(&available[..=i]); - (true, i + 1) - } - None => { - buf.extend_from_slice(available); - (false, available.len()) - } - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -fn skip_until(r: &mut R, delim: u8) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => (true, i + 1), - None => (false, available.len()), - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it -/// to perform extra ways of reading. -/// -/// For example, reading line-by-line is inefficient without using a buffer, so -/// if you want to read by line, you'll need `BufRead`, which includes a -/// [`read_line`] method as well as a [`lines`] iterator. -/// -/// # Examples -/// -/// A locked standard input implements `BufRead`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// -/// let stdin = io::stdin(); -/// for line in stdin.lock().lines() { -/// println!("{}", line?); -/// } -/// # std::io::Result::Ok(()) -/// ``` -/// -/// If you have something that implements [`Read`], you can use the [`BufReader` -/// type][`BufReader`] to turn it into a `BufRead`. -/// -/// For example, [`File`] implements [`Read`], but not `BufRead`. -/// [`BufReader`] to the rescue! -/// -/// [`File`]: crate::fs::File -/// [`read_line`]: BufRead::read_line -/// [`lines`]: BufRead::lines -/// -/// ```no_run -/// use std::io::{self, BufReader}; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let f = File::open("foo.txt")?; -/// let f = BufReader::new(f); -/// -/// for line in f.lines() { -/// let line = line?; -/// println!("{line}"); -/// } -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] -pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data, via `Read` methods, if empty. - /// - /// This is a lower-level method and is meant to be used together with [`consume`], - /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. - /// - /// [`consume`]: BufRead::consume - /// - /// Returns an empty buffer when the stream has reached EOF. - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// # Examples - /// - /// A locked standard input implements `BufRead`: - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// let buffer = stdin.fill_buf()?; - /// - /// // work with buffer - /// println!("{buffer:?}"); - /// - /// // mark the bytes we worked with as read - /// let length = buffer.len(); - /// stdin.consume(length); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fill_buf(&mut self) -> Result<&[u8]>; - - /// Marks the given `amount` of additional bytes from the internal buffer as having been read. - /// Subsequent calls to `read` only return bytes that have not been marked as read. - /// - /// This is a lower-level method and is meant to be used together with [`fill_buf`], - /// which can be used to fill the internal buffer via `Read` methods. - /// - /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. - /// - /// # Examples - /// - /// Since `consume()` is meant to be used with [`fill_buf`], - /// that method's example includes an example of `consume()`. - /// - /// [`fill_buf`]: BufRead::fill_buf - #[stable(feature = "rust1", since = "1.0.0")] - fn consume(&mut self, amount: usize); - - /// Checks if there is any data left to be `read`. - /// - /// This function may fill the buffer to check for data, - /// so this function returns `Result`, not `bool`. - /// - /// The default implementation calls `fill_buf` and checks that the - /// returned slice is empty (which means that there is no data left, - /// since EOF is reached). - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// Examples - /// - /// ``` - /// #![feature(buf_read_has_data_left)] - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// while stdin.has_data_left()? { - /// let mut line = String::new(); - /// stdin.read_line(&mut line)?; - /// // work with line - /// println!("{line:?}"); - /// } - /// # std::io::Result::Ok(()) - /// ``` - #[unstable(feature = "buf_read_has_data_left", issue = "86423")] - fn has_data_left(&mut self) -> Result { - self.fill_buf().map(|b| !b.is_empty()) - } - - /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - /// - /// This function will read bytes from the underlying stream until the - /// delimiter or EOF is found. Once found, all bytes up to, and including, - /// the delimiter (if found) will be appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the bytes in a byte slice - /// in hyphen delimited segments: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); - /// let mut buf = vec![]; - /// - /// // cursor is at 'l' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 6); - /// assert_eq!(buf, b"lorem-"); - /// buf.clear(); - /// - /// // cursor is at 'i' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 5); - /// assert_eq!(buf, b"ipsum"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, b""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - read_until(self, byte, buf) - } - - /// Skips all bytes until the delimiter `byte` or EOF is reached. - /// - /// This function will read (and discard) bytes from the underlying stream until the - /// delimiter or EOF is found. - /// - /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte if found. - /// - /// This is useful for efficiently skipping data such as NUL-terminated strings - /// in binary file formats without buffering. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read some NUL-terminated information - /// about Ferris from a binary string, skipping the fun fact: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); - /// - /// // read name - /// let mut name = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut name) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 7); - /// assert_eq!(name, b"Ferris\0"); - /// - /// // skip fun fact - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 30); - /// - /// // read animal type - /// let mut animal = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut animal) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 11); - /// assert_eq!(animal, b"Crustacean\0"); - /// - /// // reach EOF - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 1); - /// ``` - #[stable(feature = "bufread_skip_until", since = "1.83.0")] - fn skip_until(&mut self, byte: u8) -> Result { - skip_until(self, byte) - } - - /// Reads all bytes until a newline (the `0xA` byte) is reached, and append - /// them to the provided `String` buffer. - /// - /// Previous content of the buffer will be preserved. To avoid appending to - /// the buffer, you need to [`clear`] it first. - /// - /// This function will read bytes from the underlying stream until the - /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes - /// up to, and including, the delimiter (if found) will be appended to - /// `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// If this function returns [`Ok(0)`], the stream has reached EOF. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending a newline - /// or EOF. You can use [`take`] to limit the maximum number of bytes read. - /// - /// [`Ok(0)`]: Ok - /// [`clear`]: String::clear - /// [`take`]: crate::io::Read::take - /// - /// # Errors - /// - /// This function has the same error semantics as [`read_until`] and will - /// also return an error if the read bytes are not valid UTF-8. If an I/O - /// error is encountered then `buf` may contain some bytes already read in - /// the event that all data read so far was valid UTF-8. - /// - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the lines in a byte slice: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"foo\nbar"); - /// let mut buf = String::new(); - /// - /// // cursor is at 'f' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 4); - /// assert_eq!(buf, "foo\n"); - /// buf.clear(); - /// - /// // cursor is at 'b' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 3); - /// assert_eq!(buf, "bar"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, ""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_line(&mut self, buf: &mut String) -> Result { - // Note that we are not calling the `.read_until` method here, but - // rather our hardcoded implementation. For more details as to why, see - // the comments in `default_read_to_string`. - unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } - } - - /// Returns an iterator over the contents of this reader split on the byte - /// `byte`. - /// - /// The iterator returned from this function will return instances of - /// [io::Result]<[Vec]\>. Each vector returned will *not* have - /// the delimiter byte at the end. - /// - /// This function will yield errors whenever [`read_until`] would have - /// also yielded an error. - /// - /// [io::Result]: self::Result "io::Result" - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all hyphen delimited - /// segments in a byte slice - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - /// - /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); - /// assert_eq!(split_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { buf: self, delim: byte } - } - - /// Returns an iterator over the lines of this reader. - /// - /// The iterator returned from this function will yield instances of - /// [io::Result]<[String]>. Each string returned will *not* have a newline - /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. - /// - /// [io::Result]: self::Result "io::Result" - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all the lines in a byte - /// slice. - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); - /// - /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); - /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); - /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); - /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); - /// assert_eq!(lines_iter.next(), None); - /// ``` - /// - /// # Errors - /// - /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. - #[stable(feature = "rust1", since = "1.0.0")] - fn lines(self) -> Lines - where - Self: Sized, - { - Lines { buf: self } - } -} - -#[stable(feature = "chain_bufread", since = "1.9.0")] -impl BufRead for Chain { - fn fill_buf(&mut self) -> Result<&[u8]> { - if !self.done_first { - match self.first.fill_buf()? { - buf if buf.is_empty() => self.done_first = true, - buf => return Ok(buf), - } - } - self.second.fill_buf() - } - - fn consume(&mut self, amt: usize) { - if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } - } - - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - let mut read = 0; - if !self.done_first { - let n = self.first.read_until(byte, buf)?; - read += n; - - match buf.last() { - Some(b) if *b == byte && n != 0 => return Ok(read), - _ => self.done_first = true, - } - } - read += self.second.read_until(byte, buf)?; - Ok(read) - } - - // We don't override `read_line` here because an UTF-8 sequence could be - // split between the two parts of the chain -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Take { - fn fill_buf(&mut self) -> Result<&[u8]> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(&[]); - } - - let buf = self.inner.fill_buf()?; - let cap = cmp::min(buf.len() as u64, self.limit) as usize; - Ok(&buf[..cap]) - } - - fn consume(&mut self, amt: usize) { - // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; - self.inner.consume(amt); - } -} - -/// An iterator over the contents of an instance of `BufRead` split on a -/// particular byte. -/// -/// This struct is generally created by calling [`split`] on a `BufRead`. -/// Please see the documentation of [`split`] for more details. -/// -/// [`split`]: BufRead::split -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSplit")] -pub struct Split { - buf: B, - delim: u8, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Split { - type Item = Result>; - - fn next(&mut self) -> Option>> { - let mut buf = Vec::new(); - match self.buf.read_until(self.delim, &mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf[buf.len() - 1] == self.delim { - buf.pop(); - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} - -/// An iterator over the lines of an instance of `BufRead`. -/// -/// This struct is generally created by calling [`lines`] on a `BufRead`. -/// Please see the documentation of [`lines`] for more details. -/// -/// [`lines`]: BufRead::lines -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] -pub struct Lines { - buf: B, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Lines { - type Item = Result; - - fn next(&mut self) -> Option> { - let mut buf = String::new(); - match self.buf.read_line(&mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 936727f5fbc62..87c2771955a9d 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -1,37 +1,2 @@ -#![allow(missing_copy_implementations)] - #[cfg(test)] mod tests; - -use crate::io::{self, BufRead, Empty}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Empty { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(&[]) - } - - #[inline] - fn consume(&mut self, _n: usize) {} - - #[inline] - fn has_data_left(&mut self) -> io::Result { - Ok(false) - } - - #[inline] - fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { - Ok(0) - } - - #[inline] - fn skip_until(&mut self, _byte: u8) -> io::Result { - Ok(0) - } - - #[inline] - fn read_line(&mut self, _buf: &mut String) -> io::Result { - Ok(0) - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index a3921dd994d30..b6fb1fc66ccfa 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -393,6 +393,7 @@ // tidy-alphabetical-start #![feature(alloc_io)] #![feature(allocator_api)] +#![feature(buf_read_has_data_left)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] diff --git a/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html b/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html index e8ab061e679dd..8e9465ef6126b 100644 --- a/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html +++ b/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html @@ -5,7 +5,7 @@ // Provided methods fn func(self) where Self: Sized { ... } - fn lines(self) -> Lines<Self> + fn lines(self) -> Lines<Self> where Self: Sized { ... } fn merge<T>(self, a: T) where Self: Sized, From 12d0c907cc16f871e326e88eb0c8ee5c058e03e0 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:29:26 +1000 Subject: [PATCH 31/43] Fix documentation links to `BufRead` --- library/alloc/src/io/read.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index da3c2cf17413a..a1fe9fd35a9b0 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -22,8 +22,10 @@ use crate::vec::Vec; /// trait. /// /// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements `BufRead`, such as /// `BufReader`, will be more efficient. +/// therefore, using something that implements [`BufRead`], such as +/// +/// [`BufRead`]: crate::io::BufRead /// /// Repeated calls to the reader use the same cursor, so for example /// calling `read_to_end` twice on a `File` will only return the file's From 304c07e46499d7d35cb2a3cd8014019b768a95bd Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 14:10:14 +1000 Subject: [PATCH 32/43] Move `std::io::buffered` to `alloc::io` --- .../src/io/buffered/bufreader.rs | 74 ++++++- .../src/io/buffered/bufreader/buffer.rs | 13 +- .../src/io/buffered/bufwriter.rs | 32 ++- .../src/io/buffered/linewriter.rs | 2 + .../src/io/buffered/linewritershim.rs | 4 +- library/alloc/src/io/buffered/mod.rs | 189 ++++++++++++++++++ library/alloc/src/io/mod.rs | 2 + library/std/src/io/buffered/mod.rs | 185 ----------------- library/std/src/io/mod.rs | 17 +- .../mut-borrow-needed-by-trait.stderr | 4 +- .../ui/suggestions/suggest-change-mut.stderr | 2 +- 11 files changed, 305 insertions(+), 219 deletions(-) rename library/{std => alloc}/src/io/buffered/bufreader.rs (87%) rename library/{std => alloc}/src/io/buffered/bufreader/buffer.rs (93%) rename library/{std => alloc}/src/io/buffered/bufwriter.rs (95%) rename library/{std => alloc}/src/io/buffered/linewriter.rs (98%) rename library/{std => alloc}/src/io/buffered/linewritershim.rs (99%) create mode 100644 library/alloc/src/io/buffered/mod.rs diff --git a/library/std/src/io/buffered/bufreader.rs b/library/alloc/src/io/buffered/bufreader.rs similarity index 87% rename from library/std/src/io/buffered/bufreader.rs rename to library/alloc/src/io/buffered/bufreader.rs index eadc634b68273..21ad43c593bec 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -1,12 +1,14 @@ mod buffer; -use buffer::Buffer; +pub(super) use buffer::Buffer; use crate::fmt; use crate::io::{ self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint, SpecReadByte, uninlined_slow_read_byte, }; +use crate::string::String; +use crate::vec::Vec; /// The `BufReader` struct adds buffering to any reader. /// @@ -27,8 +29,8 @@ use crate::io::{ /// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause /// data loss. /// -/// [`TcpStream::read`]: crate::net::TcpStream::read -/// [`TcpStream`]: crate::net::TcpStream +/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// /// # Examples /// @@ -70,16 +72,21 @@ impl BufReader { /// Ok(()) /// } /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn new(inner: R) -> BufReader { BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } - pub(crate) fn try_new_buffer() -> io::Result { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn try_new_buffer() -> io::Result { Buffer::try_with_capacity(DEFAULT_BUF_SIZE) } - pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn with_buffer(inner: R, buf: Buffer) -> Self { Self { inner, buf } } @@ -99,6 +106,7 @@ impl BufReader { /// Ok(()) /// } /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: R) -> BufReader { BufReader { inner, buf: Buffer::with_capacity(capacity) } @@ -280,14 +288,17 @@ impl BufReader { /// Invalidates all data in the internal buffer. #[inline] - pub(in crate::io) fn discard_buffer(&mut self) { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn discard_buffer(&mut self) { self.buf.discard_buffer() } } // This is only used by a test which asserts that the initialization-tracking is correct. -#[cfg(test)] impl BufReader { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[allow(missing_docs)] pub fn initialized(&self) -> bool { self.buf.initialized() @@ -413,7 +424,28 @@ impl Read for BufReader { fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let inner_buf = self.buffer(); buf.try_reserve(inner_buf.len())?; - buf.extend_from_slice(inner_buf); + + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * inner_buf and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + unsafe { + let count = inner_buf.len(); + let len = buf.len(); + let src = inner_buf.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.extend_from_slice(inner_buf); + } + } + let nread = inner_buf.len(); self.discard_buffer(); Ok(nread + self.inner.read_to_end(buf)?) @@ -445,7 +477,31 @@ impl Read for BufReader { let mut bytes = Vec::new(); self.read_to_end(&mut bytes)?; let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?; - *buf += string; + + cfg_select! { + no_global_oom_handling => { + // SAFETY: + // * string and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + // * buf is appended with valid UTF-8 data and is initially valid UTF-8, therefore + // it is valid UTF-8 at all times. + unsafe { + let buf = buf.as_mut_vec(); + let count = string.len(); + let len = buf.len(); + let src = string.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + *buf += string; + } + } + Ok(string.len()) } } diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/alloc/src/io/buffered/bufreader/buffer.rs similarity index 93% rename from library/std/src/io/buffered/bufreader/buffer.rs rename to library/alloc/src/io/buffered/bufreader/buffer.rs index f2efcc5ab8601..a267208987238 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/alloc/src/io/buffered/bufreader/buffer.rs @@ -9,10 +9,13 @@ //! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so //! without encountering any runtime bounds checks. -use crate::cmp; +use core::cmp; +use core::mem::MaybeUninit; + +use crate::boxed::Box; use crate::io::{self, BorrowedBuf, ErrorKind, Read}; -use crate::mem::MaybeUninit; +#[expect(missing_debug_implementations)] pub struct Buffer { // The buffer. buf: Box<[MaybeUninit]>, @@ -29,12 +32,15 @@ pub struct Buffer { } impl Buffer { + #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity(capacity: usize) -> Self { let buf = Box::new_uninit_slice(capacity); Self { buf, pos: 0, filled: 0, initialized: false } } + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[inline] pub fn try_with_capacity(capacity: usize) -> io::Result { match Box::try_new_uninit_slice(capacity) { @@ -68,7 +74,8 @@ impl Buffer { } // This is only used by a test which asserts that the initialization-tracking is correct. - #[cfg(test)] + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn initialized(&self) -> bool { self.initialized } diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs similarity index 95% rename from library/std/src/io/buffered/bufwriter.rs rename to library/alloc/src/io/buffered/bufwriter.rs index 1a5cc911c0e43..4b2a4ca701f69 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/alloc/src/io/buffered/bufwriter.rs @@ -1,8 +1,10 @@ +use core::mem::{self, ManuallyDrop}; +use core::{error, fmt, ptr}; + use crate::io::{ self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, }; -use crate::mem::{self, ManuallyDrop}; -use crate::{error, fmt, ptr}; +use crate::vec::Vec; /// Wraps a writer and buffers its output. /// @@ -60,8 +62,8 @@ use crate::{error, fmt, ptr}; /// together by the buffer and will all be written out in one system call when /// the `stream` is flushed. /// -/// [`TcpStream::write`]: crate::net::TcpStream::write -/// [`TcpStream`]: crate::net::TcpStream +/// [`TcpStream::write`]: ../../std/net/struct.TcpStream.html#method.write +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// [`flush`]: BufWriter::flush #[stable(feature = "rust1", since = "1.0.0")] pub struct BufWriter { @@ -87,20 +89,26 @@ impl BufWriter { /// use std::io::BufWriter; /// use std::net::TcpStream; /// + /// # #[expect(unused_mut)] /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn new(inner: W) -> BufWriter { BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } - pub(crate) fn try_new_buffer() -> io::Result> { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn try_new_buffer() -> io::Result> { Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") }) } - pub(crate) fn with_buffer(inner: W, buf: Vec) -> Self { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn with_buffer(inner: W, buf: Vec) -> Self { Self { inner, buf, panicked: false } } @@ -115,8 +123,10 @@ impl BufWriter { /// use std::net::TcpStream; /// /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// # #[expect(unused_mut)] /// let mut buffer = BufWriter::with_capacity(100, stream); /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false } @@ -136,6 +146,7 @@ impl BufWriter { /// use std::io::BufWriter; /// use std::net::TcpStream; /// + /// # #[expect(unused_mut)] /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); /// /// // unwrap the TcpStream and flush the buffer @@ -192,7 +203,9 @@ impl BufWriter { /// "successfully written" (by returning nonzero success values from /// `write`), any 0-length writes from `inner` must be reported as i/o /// errors from this method. - pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn flush_buf(&mut self) -> io::Result<()> { // SAFETY: `::copy_from` assumes that // this will not de-initialize any elements of `self.buf`'s spare // capacity. @@ -287,6 +300,7 @@ impl BufWriter { /// use std::io::BufWriter; /// use std::net::TcpStream; /// + /// # #[expect(unused_mut)] /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); /// /// // we can use reference just like buffer @@ -343,7 +357,9 @@ impl BufWriter { /// That the buffer is a `Vec` is an implementation detail. /// Callers should not modify the capacity as there currently is no public API to do so /// and thus any capacity changes would be unexpected by the user. - pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn buffer_mut(&mut self) -> &mut Vec { &mut self.buf } diff --git a/library/std/src/io/buffered/linewriter.rs b/library/alloc/src/io/buffered/linewriter.rs similarity index 98% rename from library/std/src/io/buffered/linewriter.rs rename to library/alloc/src/io/buffered/linewriter.rs index cc6921b86dd0b..bdb979e931f2e 100644 --- a/library/std/src/io/buffered/linewriter.rs +++ b/library/alloc/src/io/buffered/linewriter.rs @@ -84,6 +84,7 @@ impl LineWriter { /// Ok(()) /// } /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn new(inner: W) -> LineWriter { // Lines typically aren't that long, don't use a giant buffer @@ -105,6 +106,7 @@ impl LineWriter { /// Ok(()) /// } /// ``` + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { LineWriter { inner: BufWriter::with_capacity(capacity, inner) } diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/alloc/src/io/buffered/linewritershim.rs similarity index 99% rename from library/std/src/io/buffered/linewritershim.rs rename to library/alloc/src/io/buffered/linewritershim.rs index 967e24812b9ff..eb11ff820fd40 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/alloc/src/io/buffered/linewritershim.rs @@ -13,12 +13,12 @@ use crate::io::{self, BufWriter, IoSlice, Write}; /// `BufWriters` to be temporarily given line-buffering logic; this is what /// enables Stdout to be alternately in line-buffered or block-buffered mode. #[derive(Debug)] -pub struct LineWriterShim<'a, W: ?Sized + Write> { +pub(super) struct LineWriterShim<'a, W: ?Sized + Write> { buffer: &'a mut BufWriter, } impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { - pub fn new(buffer: &'a mut BufWriter) -> Self { + pub(super) fn new(buffer: &'a mut BufWriter) -> Self { Self { buffer } } diff --git a/library/alloc/src/io/buffered/mod.rs b/library/alloc/src/io/buffered/mod.rs new file mode 100644 index 0000000000000..1bddcfb801932 --- /dev/null +++ b/library/alloc/src/io/buffered/mod.rs @@ -0,0 +1,189 @@ +//! Buffering wrappers for I/O traits + +mod bufreader; +mod bufwriter; +mod linewriter; +mod linewritershim; + +use core::{error, fmt}; + +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use self::bufwriter::WriterPanicked; +use self::linewritershim::LineWriterShim; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; +use crate::io::Error; + +/// An error returned by [`BufWriter::into_inner`] which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// # #[expect(unused_mut)] +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// // do stuff with the stream +/// +/// // we want to get our `TcpStream` back, so let's try: +/// +/// let stream = match stream.into_inner() { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// ``` +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoInnerError(W, Error); + +impl IntoInnerError { + /// Constructs a new IntoInnerError + fn new(writer: W, error: Error) -> Self { + Self(writer, error) + } + + /// Helper to construct a new IntoInnerError; intended to help with + /// adapters that wrap other adapters + fn new_wrapped(self, f: impl FnOnce(W) -> W2) -> IntoInnerError { + let Self(writer, error) = self; + IntoInnerError::new(f(writer), error) + } + + /// Returns the error which caused the call to [`BufWriter::into_inner()`] + /// to fail. + /// + /// This error was returned when attempting to write the internal buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// # #[expect(unused_mut)] + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's log the inner error. + /// // + /// // We'll just 'log' to stdout for this example. + /// println!("{}", e.error()); + /// + /// panic!("An unexpected error occurred."); + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn error(&self) -> &Error { + &self.1 + } + + /// Returns the buffered writer instance which generated the error. + /// + /// The returned object can be used for error recovery, such as + /// re-inspecting the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// # #[expect(unused_mut)] + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's re-examine the buffer: + /// let buffer = e.into_inner(); + /// + /// // do stuff to try to recover + /// + /// // afterwards, let's just return the stream + /// buffer.into_inner().unwrap() + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> W { + self.0 + } + + /// Consumes the [`IntoInnerError`] and returns the error which caused the call to + /// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to + /// obtain ownership of the underlying error. + /// + /// # Example + /// ``` + /// use std::io::{BufWriter, ErrorKind, Write}; + /// + /// let mut not_enough_space = [0u8; 10]; + /// let mut stream = BufWriter::new(not_enough_space.as_mut()); + /// write!(stream, "this cannot be actually written").unwrap(); + /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); + /// let err = into_inner_err.into_error(); + /// assert_eq!(err.kind(), ErrorKind::WriteZero); + /// ``` + #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] + pub fn into_error(self) -> Error { + self.1 + } + + /// Consumes the [`IntoInnerError`] and returns the error which caused the call to + /// [`BufWriter::into_inner()`] to fail, and the underlying writer. + /// + /// This can be used to simply obtain ownership of the underlying error; it can also be used for + /// advanced error recovery. + /// + /// # Example + /// ``` + /// use std::io::{BufWriter, ErrorKind, Write}; + /// + /// let mut not_enough_space = [0u8; 10]; + /// let mut stream = BufWriter::new(not_enough_space.as_mut()); + /// write!(stream, "this cannot be actually written").unwrap(); + /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); + /// let (err, recovered_writer) = into_inner_err.into_parts(); + /// assert_eq!(err.kind(), ErrorKind::WriteZero); + /// assert_eq!(recovered_writer.buffer(), b"t be actually written"); + /// ``` + #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] + pub fn into_parts(self) -> (Error, W) { + (self.1, self.0) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From> for Error { + fn from(iie: IntoInnerError) -> Error { + iie.1 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for IntoInnerError {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for IntoInnerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.error().fmt(f) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 96f4ca0a8c4d5..d7897c1b11d47 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,6 +1,7 @@ //! Traits, helpers, and type definitions for core I/O functionality. mod buf_read; +mod buffered; mod cursor; mod error; mod impls; @@ -31,6 +32,7 @@ pub use core::io::{ #[unstable(feature = "alloc_io", issue = "154046")] pub use self::{ buf_read::BufRead, + buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}, read::{Read, read_to_string}, util::{Bytes, Lines, Split}, }; diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs index e36f2d92108d0..1d09ff7d8dc1c 100644 --- a/library/std/src/io/buffered/mod.rs +++ b/library/std/src/io/buffered/mod.rs @@ -1,189 +1,4 @@ //! Buffering wrappers for I/O traits -mod bufreader; -mod bufwriter; -mod linewriter; -mod linewritershim; - #[cfg(test)] mod tests; - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use bufwriter::WriterPanicked; -use linewritershim::LineWriterShim; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; -use crate::io::Error; -use crate::{error, fmt}; - -/// An error returned by [`BufWriter::into_inner`] which combines an error that -/// happened while writing out the buffer, and the buffered writer object -/// which may be used to recover from the condition. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// // do stuff with the stream -/// -/// // we want to get our `TcpStream` back, so let's try: -/// -/// let stream = match stream.into_inner() { -/// Ok(s) => s, -/// Err(e) => { -/// // Here, e is an IntoInnerError -/// panic!("An error occurred"); -/// } -/// }; -/// ``` -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoInnerError(W, Error); - -impl IntoInnerError { - /// Constructs a new IntoInnerError - fn new(writer: W, error: Error) -> Self { - Self(writer, error) - } - - /// Helper to construct a new IntoInnerError; intended to help with - /// adapters that wrap other adapters - fn new_wrapped(self, f: impl FnOnce(W) -> W2) -> IntoInnerError { - let Self(writer, error) = self; - IntoInnerError::new(f(writer), error) - } - - /// Returns the error which caused the call to [`BufWriter::into_inner()`] - /// to fail. - /// - /// This error was returned when attempting to write the internal buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's log the inner error. - /// // - /// // We'll just 'log' to stdout for this example. - /// println!("{}", e.error()); - /// - /// panic!("An unexpected error occurred."); - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn error(&self) -> &Error { - &self.1 - } - - /// Returns the buffered writer instance which generated the error. - /// - /// The returned object can be used for error recovery, such as - /// re-inspecting the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's re-examine the buffer: - /// let buffer = e.into_inner(); - /// - /// // do stuff to try to recover - /// - /// // afterwards, let's just return the stream - /// buffer.into_inner().unwrap() - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> W { - self.0 - } - - /// Consumes the [`IntoInnerError`] and returns the error which caused the call to - /// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to - /// obtain ownership of the underlying error. - /// - /// # Example - /// ``` - /// use std::io::{BufWriter, ErrorKind, Write}; - /// - /// let mut not_enough_space = [0u8; 10]; - /// let mut stream = BufWriter::new(not_enough_space.as_mut()); - /// write!(stream, "this cannot be actually written").unwrap(); - /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); - /// let err = into_inner_err.into_error(); - /// assert_eq!(err.kind(), ErrorKind::WriteZero); - /// ``` - #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] - pub fn into_error(self) -> Error { - self.1 - } - - /// Consumes the [`IntoInnerError`] and returns the error which caused the call to - /// [`BufWriter::into_inner()`] to fail, and the underlying writer. - /// - /// This can be used to simply obtain ownership of the underlying error; it can also be used for - /// advanced error recovery. - /// - /// # Example - /// ``` - /// use std::io::{BufWriter, ErrorKind, Write}; - /// - /// let mut not_enough_space = [0u8; 10]; - /// let mut stream = BufWriter::new(not_enough_space.as_mut()); - /// write!(stream, "this cannot be actually written").unwrap(); - /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); - /// let (err, recovered_writer) = into_inner_err.into_parts(); - /// assert_eq!(err.kind(), ErrorKind::WriteZero); - /// assert_eq!(recovered_writer.buffer(), b"t be actually written"); - /// ``` - #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] - pub fn into_parts(self) -> (Error, W) { - (self.1, self.0) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From> for Error { - fn from(iie: IntoInnerError) -> Error { - iie.1 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for IntoInnerError {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for IntoInnerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.error().fmt(f) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b13eb22382fac..aac56e25d8c58 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,11 +297,14 @@ #[cfg(test)] mod tests; +use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] pub use alloc_crate::io::SimpleMessage; +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use alloc_crate::io::WriterPanicked; #[unstable(feature = "io_const_error", issue = "133448")] pub use alloc_crate::io::const_error; #[stable(feature = "io_read_to_string", since = "1.65.0")] @@ -310,22 +313,19 @@ pub use alloc_crate::io::read_to_string; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - BufRead, Bytes, Chain, Cursor, Empty, Error, ErrorKind, Lines, Read, Repeat, Result, Seek, - SeekFrom, Sink, Split, Take, Write, empty, repeat, sink, + BufRead, BufReader, BufWriter, Bytes, Chain, Cursor, Empty, Error, ErrorKind, IntoInnerError, + LineWriter, Lines, Read, Repeat, Result, Seek, SeekFrom, Sink, Split, Take, Write, empty, + repeat, sink, }; #[allow(unused_imports, reason = "only used by certain target configurations")] pub(crate) use alloc_crate::io::{DEFAULT_BUF_SIZE, default_read_buf}; pub(crate) use alloc_crate::io::{ - IoHandle, SpecReadByte, append_to_string, default_read_buf_exact, default_read_exact, - default_read_to_end, default_read_to_string, default_read_vectored, default_write_vectored, - stream_len_default, uninlined_slow_read_byte, + IoHandle, SpecReadByte, default_read_to_end, default_read_to_string, default_read_vectored, + default_write_vectored, stream_len_default, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; -use alloc_crate::io::{OsFunctions, SizeHint}; -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use self::buffered::WriterPanicked; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -339,7 +339,6 @@ pub use self::stdio::{_eprint, _print}; pub use self::stdio::{set_output_capture, try_set_output_capture}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ - buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr index eadf512a63b49..5ba7fcb2479bb 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -8,7 +8,7 @@ LL | let fp = BufWriter::new(fp); | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` note: required by a bound in `BufWriter::::new` - --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL + --> $SRC_DIR/alloc/src/io/buffered/bufwriter.rs:LL:COL error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:17:14 @@ -18,7 +18,7 @@ LL | let fp = BufWriter::new(fp); | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` note: required by a bound in `BufWriter` - --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL + --> $SRC_DIR/alloc/src/io/buffered/bufwriter.rs:LL:COL error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn std::io::Write>`, but its trait bounds were not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:21:14 diff --git a/tests/ui/suggestions/suggest-change-mut.stderr b/tests/ui/suggestions/suggest-change-mut.stderr index c47ae433ab896..fc664c92ceee2 100644 --- a/tests/ui/suggestions/suggest-change-mut.stderr +++ b/tests/ui/suggestions/suggest-change-mut.stderr @@ -7,7 +7,7 @@ LL | let mut stream_reader = BufReader::new(&stream); | required by a bound introduced by this call | note: required by a bound in `BufReader::::new` - --> $SRC_DIR/std/src/io/buffered/bufreader.rs:LL:COL + --> $SRC_DIR/alloc/src/io/buffered/bufreader.rs:LL:COL help: consider removing the leading `&`-reference | LL - let mut stream_reader = BufReader::new(&stream); From 8ce22e7bc1f67a4be06135804159c854091e1a54 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 15:30:58 +1000 Subject: [PATCH 33/43] Move `std::io::copy` internals to `alloc::io` --- library/alloc/src/io/copy.rs | 238 +++++++++++++++++++++++++++++++++++ library/alloc/src/io/mod.rs | 2 + library/std/src/io/copy.rs | 230 +-------------------------------- library/std/src/io/mod.rs | 8 +- 4 files changed, 244 insertions(+), 234 deletions(-) create mode 100644 library/alloc/src/io/copy.rs diff --git a/library/alloc/src/io/copy.rs b/library/alloc/src/io/copy.rs new file mode 100644 index 0000000000000..accdcbb7aa798 --- /dev/null +++ b/library/alloc/src/io/copy.rs @@ -0,0 +1,238 @@ +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. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub 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<'_> = 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<'_> = 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/mod.rs b/library/alloc/src/io/mod.rs index d7897c1b11d47..95f3fa24a3cb3 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; @@ -39,6 +40,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + copy::generic_copy, read::{ DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, default_read_exact, 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 0feadf2b3f8bb..45411ef9ea1b7 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,9 +1,4 @@ -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::io::{Read, Result, Write, generic_copy}; use crate::sys::io::{CopyState, kernel_copy}; #[cfg(test)] @@ -71,226 +66,3 @@ where } } } - -/// 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<'_> = 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<'_> = 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 aac56e25d8c58..d87d4a943be74 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,7 +297,6 @@ #[cfg(test)] mod tests; -use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -325,15 +324,16 @@ pub(crate) use alloc_crate::io::{ }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; +use alloc_crate::io::{OsFunctions, generic_copy}; #[stable(feature = "anonymous_pipe", since = "1.87.0")] 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}; +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}; @@ -344,7 +344,7 @@ pub use self::{ }; mod buffered; -pub(crate) mod copy; +mod copy; mod cursor; mod error; mod impls; @@ -352,5 +352,3 @@ mod pipe; pub mod prelude; mod stdio; mod util; - -pub(crate) use stdio::cleanup; From aaace09df79dd9fef7e1a9b9d2da348bcb3faccb Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 08:38:41 +1000 Subject: [PATCH 34/43] Setup `core::io::prelude` --- library/core/src/io/mod.rs | 2 ++ library/core/src/io/prelude.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 library/core/src/io/prelude.rs diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 07789080a746d..b1336a8b0d544 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}; From f514aa45d571871fa35752da5bc875ed3fb80328 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 08:38:58 +1000 Subject: [PATCH 35/43] Setup `alloc::io::prelude` --- library/alloc/src/io/mod.rs | 2 ++ library/alloc/src/io/prelude.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 library/alloc/src/io/prelude.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 95f3fa24a3cb3..7bfcb126c4b04 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -6,6 +6,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}; From 93b82007ff8cf8ae7eb23c9a590809c82618d3bf Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 11:57:23 +1000 Subject: [PATCH 36/43] Reduce visibility of unstable internal items --- library/alloc/src/io/mod.rs | 15 +++++++++------ library/alloc/src/io/read.rs | 8 ++------ library/alloc/src/io/util.rs | 16 ++++------------ 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 7bfcb126c4b04..830ff68c9a4eb 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -27,11 +27,14 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_fmt, - default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, - slice_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, default_write_vectored, stream_len_default, +}; +use core::io::{ + chain, slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, take, }; +use self::read::{append_to_string, default_read_buf_exact}; +use self::util::{bytes, lines, split, uninlined_slow_read_byte}; #[unstable(feature = "alloc_io", issue = "154046")] pub use self::{ buf_read::BufRead, @@ -44,8 +47,8 @@ pub use self::{ pub use self::{ copy::generic_copy, read::{ - DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, - default_read_exact, default_read_to_end, default_read_to_string, default_read_vectored, + DEFAULT_BUF_SIZE, default_read_buf, default_read_exact, default_read_to_end, + default_read_to_string, default_read_vectored, }, - util::{SpecReadByte, bytes, lines, split, uninlined_slow_read_byte}, + util::SpecReadByte, }; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index a1fe9fd35a9b0..d8400591d7f4f 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -738,9 +738,7 @@ impl Drop for Guard<'_> { // 2. We're passing a raw buffer to the function `f`, and it is expected that // the function only *appends* bytes to the buffer. We'll get undefined // behavior if existing bytes are overwritten to have non-UTF-8 data. -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub unsafe fn append_to_string(buf: &mut String, f: F) -> Result +pub(super) unsafe fn append_to_string(buf: &mut String, f: F) -> Result where F: FnOnce(&mut Vec) -> Result, { @@ -963,9 +961,7 @@ where Ok(()) } -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn default_read_buf_exact( +pub(super) fn default_read_buf_exact( this: &mut R, mut cursor: BorrowedCursor<'_>, ) -> Result<()> { diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs index 5c2245043e066..76644d82fa7df 100644 --- a/library/alloc/src/io/util.rs +++ b/library/alloc/src/io/util.rs @@ -388,15 +388,11 @@ fn inlined_slow_read_byte(reader: &mut R) -> Option> { // Used by `BufReader::spec_read_byte`, for which the `inline(never)` is // important. #[inline(never)] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn uninlined_slow_read_byte(reader: &mut R) -> Option> { +pub(in crate::io) fn uninlined_slow_read_byte(reader: &mut R) -> Option> { inlined_slow_read_byte(reader) } -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub const fn bytes(inner: R) -> Bytes { +pub(in crate::io) const fn bytes(inner: R) -> Bytes { Bytes { inner } } @@ -434,9 +430,7 @@ impl Iterator for Split { } } -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub const fn split(buf: B, delim: u8) -> Split { +pub(in crate::io) const fn split(buf: B, delim: u8) -> Split { Split { buf, delim } } @@ -475,8 +469,6 @@ impl Iterator for Lines { } } -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub const fn lines(buf: B) -> Lines { +pub(in crate::io) const fn lines(buf: B) -> Lines { Lines { buf } } From bc86e9f2786d2c3e9a351b71415097e460ffb01c Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:18:11 +1000 Subject: [PATCH 37/43] Move general IO tests to `alloctests` --- .../io/tests.rs => alloctests/tests/io/mod.rs} | 16 ++++++++++------ library/alloctests/tests/lib.rs | 12 ++++++++++++ library/std/src/io/mod.rs | 3 --- 3 files changed, 22 insertions(+), 9 deletions(-) rename library/{std/src/io/tests.rs => alloctests/tests/io/mod.rs} (98%) 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 56d3bd678e104..3fd54f784e458 100644 --- a/library/std/src/io/tests.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,7 +1,11 @@ -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; +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 +274,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 +364,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/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 1414835058b9a..2b763c198471f 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -42,6 +42,17 @@ #![feature(vec_peek_mut)] #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] +#![feature(alloc_io)] +#![feature(borrowed_buf_init)] +#![feature(buf_read_has_data_left)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] +#![feature(cursor_split)] +#![feature(io_const_error)] +#![feature(read_buf)] +#![feature(seek_io_take_position)] +#![feature(seek_stream_len)] +#![feature(write_all_vectored)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] @@ -63,6 +74,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/std/src/io/mod.rs b/library/std/src/io/mod.rs index d87d4a943be74..ccfdc07f15eb2 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; - #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] From 59ccc3bda4dfb75814d31b7374640e87cbb71b5e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:31:20 +1000 Subject: [PATCH 38/43] Move `std::io::util` tests to `alloctests` --- library/alloctests/tests/io/mod.rs | 2 ++ .../io/util/tests.rs => alloctests/tests/io/util.rs} | 10 +++++----- library/alloctests/tests/lib.rs | 1 + library/std/src/io/mod.rs | 1 - library/std/src/io/util.rs | 2 -- 5 files changed, 8 insertions(+), 8 deletions(-) rename library/{std/src/io/util/tests.rs => alloctests/tests/io/util.rs} (96%) delete mode 100644 library/std/src/io/util.rs diff --git a/library/alloctests/tests/io/mod.rs b/library/alloctests/tests/io/mod.rs index 3fd54f784e458..be4cb3fd18930 100644 --- a/library/alloctests/tests/io/mod.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,3 +1,5 @@ +mod util; + use alloc::io::{ self, BorrowedBuf, BufRead, BufReader, Cursor, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, SeekFrom, Write, repeat, 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 fa1193f2b00fa..aff689e6714ce 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 2b763c198471f..66b627a8d0b02 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -45,6 +45,7 @@ #![feature(alloc_io)] #![feature(borrowed_buf_init)] #![feature(buf_read_has_data_left)] +#![feature(can_vector)] #![feature(core_io_borrowed_buf)] #![feature(core_io_internals)] #![feature(cursor_split)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index ccfdc07f15eb2..2a6985d154d90 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -348,4 +348,3 @@ 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; From 3a222b1b03f12f71f3502994bb462eccd0d11448 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:42:23 +1000 Subject: [PATCH 39/43] Move `std::io::cursor` tests to `alloctests` --- .../src/io/cursor/tests.rs => alloctests/tests/io/cursor.rs} | 5 +++-- library/alloctests/tests/io/mod.rs | 1 + library/std/src/io/cursor.rs | 2 -- library/std/src/io/mod.rs | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) rename library/{std/src/io/cursor/tests.rs => alloctests/tests/io/cursor.rs} (99%) delete mode 100644 library/std/src/io/cursor.rs 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/alloctests/tests/io/mod.rs b/library/alloctests/tests/io/mod.rs index be4cb3fd18930..8a1581c7fca05 100644 --- a/library/alloctests/tests/io/mod.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,3 +1,4 @@ +mod cursor; mod util; use alloc::io::{ 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/mod.rs b/library/std/src/io/mod.rs index 2a6985d154d90..f6240aca1f0b3 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -342,7 +342,6 @@ pub use self::{ mod buffered; mod copy; -mod cursor; mod error; mod impls; mod pipe; From 9294307e10ebdff4ca543a6a45e9d27408ca1b69 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:45:18 +1000 Subject: [PATCH 40/43] Move `std::io::buffered` tests to `alloctests` --- .../tests/io/buffered.rs} | 31 ++++++++++--------- library/alloctests/tests/io/mod.rs | 1 + library/std/src/io/buffered/mod.rs | 4 --- library/std/src/io/mod.rs | 1 - 4 files changed, 18 insertions(+), 19 deletions(-) rename library/{std/src/io/buffered/tests.rs => alloctests/tests/io/buffered.rs} (98%) delete mode 100644 library/std/src/io/buffered/mod.rs 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 742e753f9b951..5420b75fcc0a8 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 }, @@ -1023,7 +1026,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()) @@ -1056,7 +1059,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) @@ -1079,7 +1082,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/alloctests/tests/io/mod.rs b/library/alloctests/tests/io/mod.rs index 8a1581c7fca05..8b5a32e523644 100644 --- a/library/alloctests/tests/io/mod.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,3 +1,4 @@ +mod buffered; mod cursor; mod util; 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/mod.rs b/library/std/src/io/mod.rs index f6240aca1f0b3..b5734c2029aa6 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -340,7 +340,6 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -mod buffered; mod copy; mod error; mod impls; From 331e90b83ae9e14ade0982d62868146cc1f1510f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 25 May 2026 13:29:57 +1000 Subject: [PATCH 41/43] Move `std::io::impls` benchmarks to `alloctests` --- library/{std/src/io/impls/tests.rs => alloctests/benches/io.rs} | 2 +- library/alloctests/benches/lib.rs | 1 + library/std/src/io/impls.rs | 2 -- library/std/src/io/mod.rs | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) rename library/{std/src/io/impls/tests.rs => alloctests/benches/io.rs} (97%) delete mode 100644 library/std/src/io/impls.rs diff --git a/library/std/src/io/impls/tests.rs b/library/alloctests/benches/io.rs similarity index 97% rename from library/std/src/io/impls/tests.rs rename to library/alloctests/benches/io.rs index d1cd84a67ada5..5ee8263b8cea4 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) { diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index b7e09fc2e162b..58970195a0d86 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/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 b5734c2029aa6..23026b5c867bd 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -342,7 +342,6 @@ pub use self::{ mod copy; mod error; -mod impls; mod pipe; pub mod prelude; mod stdio; From df079fb733dc781873fa0383f9e7044211b9d974 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 26 May 2026 09:16:54 +1000 Subject: [PATCH 42/43] Reduce API surface of `Buffer` None of the currently public methods are accessible outside `std`, and are unused within. Therefore, they can be restricted to internal use. --- .../alloc/src/io/buffered/bufreader/buffer.rs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/library/alloc/src/io/buffered/bufreader/buffer.rs b/library/alloc/src/io/buffered/bufreader/buffer.rs index a267208987238..3c88e705c7011 100644 --- a/library/alloc/src/io/buffered/bufreader/buffer.rs +++ b/library/alloc/src/io/buffered/bufreader/buffer.rs @@ -15,7 +15,9 @@ use core::mem::MaybeUninit; use crate::boxed::Box; use crate::io::{self, BorrowedBuf, ErrorKind, Read}; -#[expect(missing_debug_implementations)] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[derive(Debug)] pub struct Buffer { // The buffer. buf: Box<[MaybeUninit]>, @@ -34,15 +36,13 @@ pub struct Buffer { impl Buffer { #[cfg(not(no_global_oom_handling))] #[inline] - pub fn with_capacity(capacity: usize) -> Self { + pub(super) fn with_capacity(capacity: usize) -> Self { let buf = Box::new_uninit_slice(capacity); Self { buf, pos: 0, filled: 0, initialized: false } } - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[inline] - pub fn try_with_capacity(capacity: usize) -> io::Result { + pub(super) fn try_with_capacity(capacity: usize) -> io::Result { match Box::try_new_uninit_slice(capacity) { Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: false }), Err(_) => { @@ -52,49 +52,47 @@ impl Buffer { } #[inline] - pub fn buffer(&self) -> &[u8] { + pub(super) fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and // that region is initialized because those are all invariants of this type. unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } #[inline] - pub fn capacity(&self) -> usize { + pub(super) fn capacity(&self) -> usize { self.buf.len() } #[inline] - pub fn filled(&self) -> usize { + pub(super) fn filled(&self) -> usize { self.filled } #[inline] - pub fn pos(&self) -> usize { + pub(super) fn pos(&self) -> usize { self.pos } // This is only used by a test which asserts that the initialization-tracking is correct. - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn initialized(&self) -> bool { + pub(super) fn initialized(&self) -> bool { self.initialized } #[inline] - pub fn discard_buffer(&mut self) { + pub(super) fn discard_buffer(&mut self) { self.pos = 0; self.filled = 0; } #[inline] - pub fn consume(&mut self, amt: usize) { + pub(super) fn consume(&mut self, amt: usize) { self.pos = cmp::min(self.pos + amt, self.filled); } /// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to /// `visitor` and return true. If there are not enough bytes available, return false. #[inline] - pub fn consume_with(&mut self, amt: usize, mut visitor: V) -> bool + pub(super) fn consume_with(&mut self, amt: usize, mut visitor: V) -> bool where V: FnMut(&[u8]), { @@ -109,12 +107,12 @@ impl Buffer { } #[inline] - pub fn unconsume(&mut self, amt: usize) { + pub(super) fn unconsume(&mut self, amt: usize) { self.pos = self.pos.saturating_sub(amt); } /// Read more bytes into the buffer without discarding any of its contents - pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { + pub(super) fn read_more(&mut self, mut reader: impl Read) -> io::Result { let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); if self.initialized { @@ -131,14 +129,14 @@ impl Buffer { } /// Remove bytes that have already been read from the buffer. - pub fn backshift(&mut self) { + pub(super) fn backshift(&mut self) { self.buf.copy_within(self.pos..self.filled, 0); self.filled -= self.pos; self.pos = 0; } #[inline] - pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { + pub(super) fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch // some more data from the reader. // Branch using `>=` instead of the more correct `==` From e5621e9d14e5317be3bd6201b9ed0ee077bfbd88 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 26 May 2026 09:31:29 +1000 Subject: [PATCH 43/43] Note where and why documentation hard-links are used Also link back to a relevant issue to track possible changes in functionality. --- library/alloc/src/io/buffered/bufreader.rs | 1 + library/alloc/src/io/buffered/bufwriter.rs | 1 + library/alloc/src/io/error.rs | 3 +++ library/core/src/io/cursor.rs | 2 ++ library/core/src/io/error.rs | 6 ++++++ library/core/src/io/mod.rs | 1 + 6 files changed, 14 insertions(+) diff --git a/library/alloc/src/io/buffered/bufreader.rs b/library/alloc/src/io/buffered/bufreader.rs index 21ad43c593bec..4f00d4ad41eba 100644 --- a/library/alloc/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -29,6 +29,7 @@ use crate::vec::Vec; /// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause /// data loss. /// +// FIXME(#74481): Hard-links required to link from `alloc` to `std` /// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read /// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// diff --git a/library/alloc/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs index 4b2a4ca701f69..59fa1f230fd3c 100644 --- a/library/alloc/src/io/buffered/bufwriter.rs +++ b/library/alloc/src/io/buffered/bufwriter.rs @@ -62,6 +62,7 @@ use crate::vec::Vec; /// together by the buffer and will all be written out in one system call when /// the `stream` is flushed. /// +// FIXME(#74481): Hard-links required to link from `alloc` to `std` /// [`TcpStream::write`]: ../../std/net/struct.TcpStream.html#method.write /// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// [`flush`]: BufWriter::flush diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 5f70ceece7015..4de284ebd4b77 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -55,6 +55,7 @@ impl Error { /// originate from the OS itself. It is a shortcut for [`Error::new`][new] /// with [`ErrorKind::Other`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// /// # Examples @@ -84,6 +85,7 @@ impl Error { /// then this function will return [`Some`], /// otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// [other]: struct.Error.html#method.other /// @@ -141,6 +143,7 @@ impl Error { /// `Box::downcast` on the custom boxed error, returned by /// [`Error::into_inner`][into_inner]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [into_inner]: struct.Error.html#method.into_inner /// /// # Examples diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index bdc9ea0c2c9d4..4f54b4f508d12 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -19,6 +19,7 @@ use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html @@ -83,6 +84,7 @@ impl Cursor { /// is not empty. So writing to cursor starts with overwriting [`Vec`] /// content, not with appending to it. /// + // FIXME(#74481): Hard-links required to link from `core` to `alloc` /// [`Vec`]: ../../alloc/vec/struct.Vec.html /// /// # Examples diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 98fd28e08c5f9..b08a3abc977b2 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -39,6 +39,7 @@ use crate::{error, fmt, result}; /// will generally use `io::Result` instead of shadowing the [prelude]'s import /// of [`core::result::Result`][`Result`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [`std::io`]: ../../std/io/index.html /// [`io::Error`]: Error /// [`Result`]: crate::result::Result @@ -70,6 +71,7 @@ pub type Result = result::Result; /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html /// [Write]: crate::io::Write /// [Seek]: crate::io::Seek @@ -165,6 +167,7 @@ pub struct SimpleMessage { /// Contrary to [`Error::new`][new], this macro does not allocate and can be used in /// `const` contexts. /// +// FIXME(#74481): Hard-links required to link from `core` to `alloc` for incoherent method /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Example @@ -280,6 +283,7 @@ impl Error { /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise /// it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` for incoherent method /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error /// @@ -360,6 +364,7 @@ impl Error { /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Examples @@ -435,6 +440,7 @@ impl Error { /// it will be a value inferred from the system's error encoding. /// See [`last_os_error`][last_os_error] for more details. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// /// # Examples diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index b1336a8b0d544..0b8e19ae1274b 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -54,6 +54,7 @@ pub use self::{ /// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary /// and be lost after the method has been called. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html /// [`Write`]: crate::io::Write