From 4ba32d687a79bcb235062613b0756200de6002d8 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 13 May 2026 13:27:04 +1000 Subject: [PATCH 01/15] If PR 155849 and PR 155625 were merged... --- library/alloc/src/io/error.rs | 266 +++++++ library/alloc/src/io/mod.rs | 21 + library/alloc/src/lib.rs | 8 + library/core/src/io/error.rs | 690 ++++++++++++++++- library/core/src/io/error/os_functions.rs | 30 + .../core/src/io/error/os_functions_atomic.rs | 54 ++ .../src/io/error/repr_bitpacked.rs | 54 +- .../src/io/error/repr_unpacked.rs | 12 +- library/core/src/io/io_slice.rs | 339 ++++++++ .../src/io/io_slice/repr_generic.rs} | 20 +- .../src/io/io_slice/repr_iovec.rs} | 48 +- .../src/io/io_slice/repr_uefi.rs} | 36 +- library/core/src/io/io_slice/repr_windows.rs | 99 +++ library/core/src/io/mod.rs | 21 +- library/core/src/lib.rs | 1 + library/coretests/tests/io/io_slice.rs | 100 +++ library/coretests/tests/io/mod.rs | 1 + library/coretests/tests/lib.rs | 1 + library/std/src/io/error.rs | 722 +----------------- library/std/src/io/error/tests.rs | 45 +- library/std/src/io/mod.rs | 347 +-------- library/std/src/io/tests.rs | 103 +-- library/std/src/lib.rs | 1 + library/std/src/os/unix/process.rs | 4 +- library/std/src/sys/io/io_slice/windows.rs | 82 -- library/std/src/sys/io/mod.rs | 22 - src/tools/linkchecker/main.rs | 14 + .../binary-op-not-allowed-issue-125631.stderr | 6 +- 28 files changed, 1796 insertions(+), 1351 deletions(-) create mode 100644 library/alloc/src/io/error.rs create mode 100644 library/alloc/src/io/mod.rs create mode 100644 library/core/src/io/error/os_functions.rs create mode 100644 library/core/src/io/error/os_functions_atomic.rs rename library/{std => core}/src/io/error/repr_bitpacked.rs (88%) rename library/{std => core}/src/io/error/repr_unpacked.rs (76%) create mode 100644 library/core/src/io/io_slice.rs rename library/{std/src/sys/io/io_slice/unsupported.rs => core/src/io/io_slice/repr_generic.rs} (50%) rename library/{std/src/sys/io/io_slice/iovec.rs => core/src/io/io_slice/repr_iovec.rs} (54%) rename library/{std/src/sys/io/io_slice/uefi.rs => core/src/io/io_slice/repr_uefi.rs} (56%) create mode 100644 library/core/src/io/io_slice/repr_windows.rs create mode 100644 library/coretests/tests/io/io_slice.rs delete mode 100644 library/std/src/sys/io/io_slice/windows.rs diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..8cdd28e2b1960 --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,266 @@ +use core::{error, result}; + +use crate::boxed::Box; +use crate::io::{Custom, CustomOwner, Error, ErrorKind, const_error}; + +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>, + { + error_from_box(kind, error.into()) + } + + /// 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>, + { + error_from_box(ErrorKind::Other, error.into()) + } + + /// 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))] +fn error_from_box(kind: ErrorKind, error: Box) -> Error { + /// # 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. + let custom_owner = unsafe { CustomOwner::from_raw(custom) }; + + // SAFETY: `custom_owner` has bee constructed from a `Box` from the `alloc` crate. + unsafe { Error::from_custom_owner(custom_owner) } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..327701c542494 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,21 @@ +//! 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 = "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, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, + repeat, sink, +}; +use core::io::{Custom, CustomOwner}; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use core::io::{OsFunctions, chain, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7b41023ff31bf..ea333c8d84574 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)] @@ -129,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)] @@ -142,6 +147,7 @@ #![feature(ptr_alignment_type)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(raw_os_error_ty)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] #![feature(sized_type_properties)] @@ -230,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/core/src/io/error.rs b/library/core/src/io/error.rs index 67cd1eebf44f2..317b374cdf4cc 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,6 +1,694 @@ #![unstable(feature = "core_io", issue = "154046")] -use crate::fmt; +// 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; + +use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; +use self::repr::Repr; +use crate::{error, fmt, result}; + +#[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, + }; +} + +/// 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 } }, + )) +} + +// 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 { + 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` +unsafe impl Send for Custom {} + +// SAFETY: All members of `Custom` are `Sync` +unsafe impl Sync for Custom {} + +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() + } +} + +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 + } + + 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() } + } + + 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` +unsafe impl Send for CustomOwner {} + +// SAFETY: Custom is `Sync` +unsafe impl Sync for CustomOwner {} + +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 + } + + #[allow(dead_code, reason = "only required 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() } + } + + #[allow(dead_code, reason = "only required 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` + unsafe { self.0.as_mut() } + } +} + +/// 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::(); +} /// The type of raw OS error codes. /// 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..b34b7c7dc7926 --- /dev/null +++ b/library/core/src/io/error/os_functions.rs @@ -0,0 +1,30 @@ +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] +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] +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] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = OsFunctions::DEFAULT; + (f.decode_error_kind)(errno) +} + +#[inline] +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 new file mode 100644 index 0000000000000..762b011c3f88c --- /dev/null +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -0,0 +1,54 @@ +//! 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] +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); +} + +#[inline] +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] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = get_os_functions(); + (f.decode_error_kind)(errno) +} + +#[inline] +pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = get_os_functions(); + (f.is_interrupted)(errno) +} diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs similarity index 88% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/core/src/io/error/repr_bitpacked.rs index 7c237825459af..f01bb049aa780 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/core/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,16 @@ 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.0.as_ptr().cast::(); + core::mem::forget(b); // Should only be possible if an allocator handed out a pointer with // wrong alignment. debug_assert_eq!(p.addr() & TAG_MASK, 0); @@ -146,7 +147,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). @@ -156,7 +158,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"); @@ -166,11 +169,9 @@ 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. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. + 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!( @@ -183,11 +184,9 @@ 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. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + 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!( @@ -200,38 +199,39 @@ 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) + // SAFETY: References are never null. + let ptr = unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }; + Self(ptr, 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> { + 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)) } + unsafe { decode_repr(this.0, |p| CustomOwner(core::ptr::NonNull::new_unchecked(p))) } } } 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)); + let _ = decode_repr(self.0, |p| CustomOwner(core::ptr::NonNull::new_unchecked(p))); } } } @@ -255,7 +255,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 @@ -306,7 +306,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/core/src/io/error/repr_unpacked.rs similarity index 76% rename from library/std/src/io/error/repr_unpacked.rs rename to library/core/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..a32ed58ce1b74 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/core/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()), } } } diff --git a/library/core/src/io/io_slice.rs b/library/core/src/io/io_slice.rs new file mode 100644 index 0000000000000..0bdd410d3e964 --- /dev/null +++ b/library/core/src/io/io_slice.rs @@ -0,0 +1,339 @@ +use crate::fmt; +use crate::mem::take; +use crate::ops::{Deref, DerefMut}; + +cfg_select! { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { + #[path = "io_slice/repr_iovec.rs"] + mod repr; + } + target_os = "windows" => { + #[path = "io_slice/repr_windows.rs"] + mod repr; + } + target_os = "uefi" => { + #[path = "io_slice/repr_uefi.rs"] + mod repr; + } + _ => { + #[path = "io_slice/repr_generic.rs"] + mod repr; + } +} + +/// A buffer type used with `Read::read_vectored`. +/// +/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be +/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on +/// Windows. +#[stable(feature = "iovec", since = "1.36.0")] +#[repr(transparent)] +pub struct IoSliceMut<'a>(repr::IoSliceMut<'a>); + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSliceMut<'a> {} + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSliceMut<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSliceMut<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSliceMut<'a> { + /// Creates a new `IoSliceMut` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(repr::IoSliceMut::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of + /// multiple buffers. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slice. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSliceMut; + /// use std::ops::Deref; + /// + /// let mut data = [1; 8]; + /// let mut buf = IoSliceMut::new(&mut data); + /// + /// // Mark 3 bytes as read. + /// buf.advance(3); + /// assert_eq!(buf.deref(), [1; 5].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance(&mut self, n: usize) { + self.0.advance(n) + } + + /// Advance a slice of slices. + /// + /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over. + /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified + /// to start at that cursor. + /// + /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes, + /// the result will only include the second `IoSliceMut`, advanced by 2 bytes. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slices. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSliceMut; + /// use std::ops::Deref; + /// + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// + /// // Mark 10 bytes as read. + /// IoSliceMut::advance_slices(&mut bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { + // Number of buffers to remove. + let mut remove = 0; + // Remaining length before reaching n. + let mut left = n; + for buf in bufs.iter() { + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; + remove += 1; + } else { + break; + } + } + + *bufs = &mut take(bufs)[remove..]; + if bufs.is_empty() { + assert!(left == 0, "advancing io slices beyond their length"); + } else { + bufs[0].advance(left); + } + } + + /// Get the underlying bytes as a mutable slice with the original lifetime. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSliceMut; + /// + /// let mut data = *b"abcdef"; + /// let io_slice = IoSliceMut::new(&mut data); + /// io_slice.into_slice()[0] = b'A'; + /// + /// assert_eq!(&data, b"Abcdef"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn into_slice(self) -> &'a mut [u8] { + self.0.into_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSliceMut<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> DerefMut for IoSliceMut<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } +} + +/// A buffer type used with `Write::write_vectored`. +/// +/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be +/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on +/// Windows. +#[stable(feature = "iovec", since = "1.36.0")] +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a>(repr::IoSlice<'a>); + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSlice<'a> {} + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSlice<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSlice<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSlice<'a> { + /// Creates a new `IoSlice` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[must_use] + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(repr::IoSlice::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple + /// buffers. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slice. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSlice; + /// use std::ops::Deref; + /// + /// let data = [1; 8]; + /// let mut buf = IoSlice::new(&data); + /// + /// // Mark 3 bytes as read. + /// buf.advance(3); + /// assert_eq!(buf.deref(), [1; 5].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance(&mut self, n: usize) { + self.0.advance(n) + } + + /// Advance a slice of slices. + /// + /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over. + /// If the cursor ends up in the middle of an `IoSlice`, it is modified + /// to start at that cursor. + /// + /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes, + /// the result will only include the second `IoSlice`, advanced by 2 bytes. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slices. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSlice; + /// use std::ops::Deref; + /// + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// + /// // Mark 10 bytes as written. + /// IoSlice::advance_slices(&mut bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { + // Number of buffers to remove. + let mut remove = 0; + // Remaining length before reaching n. This prevents overflow + // that could happen if the length of slices in `bufs` were instead + // accumulated. Those slice may be aliased and, if they are large + // enough, their added length may overflow a `usize`. + let mut left = n; + for buf in bufs.iter() { + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; + remove += 1; + } else { + break; + } + } + + *bufs = &mut take(bufs)[remove..]; + if bufs.is_empty() { + assert!(left == 0, "advancing io slices beyond their length"); + } else { + bufs[0].advance(left); + } + } + + /// Get the underlying bytes as a slice with the original lifetime. + /// + /// This doesn't borrow from `self`, so is less restrictive than calling + /// `.deref()`, which does. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSlice; + /// + /// let data = b"abcdef"; + /// + /// let mut io_slice = IoSlice::new(data); + /// let tail = &io_slice.as_slice()[3..]; + /// + /// // This works because `tail` doesn't borrow `io_slice` + /// io_slice = IoSlice::new(tail); + /// + /// assert_eq!(io_slice.as_slice(), b"def"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn as_slice(self) -> &'a [u8] { + self.0.as_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSlice<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} diff --git a/library/std/src/sys/io/io_slice/unsupported.rs b/library/core/src/io/io_slice/repr_generic.rs similarity index 50% rename from library/std/src/sys/io/io_slice/unsupported.rs rename to library/core/src/io/io_slice/repr_generic.rs index 1572cac6cd771..fdeb4978aedea 100644 --- a/library/std/src/sys/io/io_slice/unsupported.rs +++ b/library/core/src/io/io_slice/repr_generic.rs @@ -1,52 +1,52 @@ use crate::mem; #[derive(Copy, Clone)] -pub struct IoSlice<'a>(&'a [u8]); +pub(crate) struct IoSlice<'a>(&'a [u8]); impl<'a> IoSlice<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + pub(crate) fn new(buf: &'a [u8]) -> IoSlice<'a> { IoSlice(buf) } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { self.0 = &self.0[n..] } #[inline] - pub const fn as_slice(&self) -> &'a [u8] { + pub(crate) const fn as_slice(&self) -> &'a [u8] { self.0 } } -pub struct IoSliceMut<'a>(&'a mut [u8]); +pub(crate) struct IoSliceMut<'a>(&'a mut [u8]); impl<'a> IoSliceMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { IoSliceMut(buf) } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { let slice = mem::take(&mut self.0); let (_, remaining) = slice.split_at_mut(n); self.0 = remaining; } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub(crate) fn as_slice(&self) -> &[u8] { self.0 } #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { + pub(crate) const fn into_slice(self) -> &'a mut [u8] { self.0 } #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { + pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { self.0 } } diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/core/src/io/io_slice/repr_iovec.rs similarity index 54% rename from library/std/src/sys/io/io_slice/iovec.rs rename to library/core/src/io/io_slice/repr_iovec.rs index d549aca250d5f..cd8f5d539ecce 100644 --- a/library/std/src/sys/io/io_slice/iovec.rs +++ b/library/core/src/io/io_slice/repr_iovec.rs @@ -1,24 +1,24 @@ -#[cfg(target_os = "hermit")] -use hermit_abi::iovec; -#[cfg(any(target_family = "unix", target_os = "trusty", target_os = "wasi"))] -use libc::iovec; - use crate::ffi::c_void; use crate::marker::PhantomData; use crate::slice; -#[cfg(target_os = "solid_asp3")] -use crate::sys::pal::abi::sockets::iovec; + +#[derive(Copy, Clone)] +#[repr(C)] +struct iovec { + iov_base: *mut c_void, + iov_len: usize, +} #[derive(Copy, Clone)] #[repr(transparent)] -pub struct IoSlice<'a> { +pub(crate) struct IoSlice<'a> { vec: iovec, _p: PhantomData<&'a [u8]>, } impl<'a> IoSlice<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + pub(crate) fn new(buf: &'a [u8]) -> IoSlice<'a> { IoSlice { vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, _p: PhantomData, @@ -26,11 +26,14 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { if self.vec.iov_len < n { panic!("advancing IoSlice beyond its length"); } + // SAFETY: + // * `n <= iov_len` as asserted above. + // * The allocation pointed to by `iov_base` is valid up to `iov_base + iov_len`. unsafe { self.vec.iov_len -= n; self.vec.iov_base = self.vec.iov_base.add(n); @@ -38,20 +41,22 @@ impl<'a> IoSlice<'a> { } #[inline] - pub const fn as_slice(&self) -> &'a [u8] { + pub(crate) const fn as_slice(&self) -> &'a [u8] { + // SAFETY: + // * `iov_base` and `iov_len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } #[repr(transparent)] -pub struct IoSliceMut<'a> { +pub(crate) struct IoSliceMut<'a> { vec: iovec, _p: PhantomData<&'a mut [u8]>, } impl<'a> IoSliceMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { IoSliceMut { vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, _p: PhantomData, @@ -59,11 +64,14 @@ impl<'a> IoSliceMut<'a> { } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { if self.vec.iov_len < n { panic!("advancing IoSliceMut beyond its length"); } + // SAFETY: + // * `n <= iov_len` as asserted above. + // * The allocation pointed to by `iov_base` is valid up to `iov_base + iov_len`. unsafe { self.vec.iov_len -= n; self.vec.iov_base = self.vec.iov_base.add(n); @@ -71,17 +79,23 @@ impl<'a> IoSliceMut<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub(crate) fn as_slice(&self) -> &[u8] { + // SAFETY: + // * `iov_base` and `iov_len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { + pub(crate) const fn into_slice(self) -> &'a mut [u8] { + // SAFETY: + // * `iov_base` and `iov_len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { + pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { + // SAFETY: + // * `iov_base` and `iov_len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } diff --git a/library/std/src/sys/io/io_slice/uefi.rs b/library/core/src/io/io_slice/repr_uefi.rs similarity index 56% rename from library/std/src/sys/io/io_slice/uefi.rs rename to library/core/src/io/io_slice/repr_uefi.rs index 909cfbea0b7ba..3698eea733837 100644 --- a/library/std/src/sys/io/io_slice/uefi.rs +++ b/library/core/src/io/io_slice/repr_uefi.rs @@ -6,7 +6,7 @@ use crate::slice; #[derive(Copy, Clone)] #[repr(C)] -pub struct IoSlice<'a> { +pub(crate) struct IoSlice<'a> { len: u32, data: *const u8, _p: PhantomData<&'a [u8]>, @@ -14,28 +14,34 @@ pub struct IoSlice<'a> { impl<'a> IoSlice<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + pub(crate) fn new(buf: &'a [u8]) -> IoSlice<'a> { let len = buf.len().try_into().unwrap(); Self { len, data: buf.as_ptr(), _p: PhantomData } } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { self.len = u32::try_from(n) .ok() .and_then(|n| self.len.checked_sub(n)) .expect("advancing IoSlice beyond its length"); + + // SAFETY: + // * `n <= len` as asserted above. + // * The allocation pointed to by `data` is valid up to `data + len`. unsafe { self.data = self.data.add(n) }; } #[inline] - pub const fn as_slice(&self) -> &'a [u8] { + pub(crate) const fn as_slice(&self) -> &'a [u8] { + // SAFETY: + // * `data` and `len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts(self.data, self.len as usize) } } } #[repr(C)] -pub struct IoSliceMut<'a> { +pub(crate) struct IoSliceMut<'a> { len: u32, data: *mut u8, _p: PhantomData<&'a mut [u8]>, @@ -43,32 +49,42 @@ pub struct IoSliceMut<'a> { impl<'a> IoSliceMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { let len = buf.len().try_into().unwrap(); Self { len, data: buf.as_mut_ptr(), _p: PhantomData } } #[inline] - pub fn advance(&mut self, n: usize) { + pub(crate) fn advance(&mut self, n: usize) { self.len = u32::try_from(n) .ok() .and_then(|n| self.len.checked_sub(n)) .expect("advancing IoSlice beyond its length"); + + // SAFETY: + // * `n <= len` as asserted above. + // * The allocation pointed to by `data` is valid up to `data + len`. unsafe { self.data = self.data.add(n) }; } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub(crate) fn as_slice(&self) -> &[u8] { + // SAFETY: + // * `data` and `len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts(self.data, self.len as usize) } } #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { + pub(crate) const fn into_slice(self) -> &'a mut [u8] { + // SAFETY: + // * `data` and `len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } } #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { + pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { + // SAFETY: + // * `data` and `len` come from a prior decomposition of a valid slice. unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } } } diff --git a/library/core/src/io/io_slice/repr_windows.rs b/library/core/src/io/io_slice/repr_windows.rs new file mode 100644 index 0000000000000..6162d86de8e59 --- /dev/null +++ b/library/core/src/io/io_slice/repr_windows.rs @@ -0,0 +1,99 @@ +use crate::marker::PhantomData; +use crate::slice; + +#[repr(C)] +#[derive(Clone, Copy)] +struct WSABUF { + len: u32, + buf: *mut u8, +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub(crate) struct IoSlice<'a> { + vec: WSABUF, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub(crate) fn new(buf: &'a [u8]) -> IoSlice<'a> { + assert!(buf.len() <= u32::MAX as usize); + IoSlice { + vec: WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, + _p: PhantomData, + } + } + + #[inline] + pub(crate) fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSlice beyond its length"); + } + + // SAFETY: + // * `n <= len` as asserted above. + // * The allocation pointed to by `buf` is valid up to `buf + len`. + unsafe { + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub(crate) const fn as_slice(&self) -> &'a [u8] { + // SAFETY: + // * `buf` and `len` come from a prior decomposition of a valid slice. + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } + } +} + +#[repr(transparent)] +pub(crate) struct IoSliceMut<'a> { + vec: WSABUF, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub(crate) fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + assert!(buf.len() <= u32::MAX as usize); + IoSliceMut { vec: WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData } + } + + #[inline] + pub(crate) fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSliceMut beyond its length"); + } + + // SAFETY: + // * `n <= len` as asserted above. + // * The allocation pointed to by `buf` is valid up to `buf + len`. + unsafe { + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub(crate) fn as_slice(&self) -> &[u8] { + // SAFETY: + // * `buf` and `len` come from a prior decomposition of a valid slice. + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } + } + + #[inline] + pub(crate) const fn into_slice(self) -> &'a mut [u8] { + // SAFETY: + // * `buf` and `len` come from a prior decomposition of a valid slice. + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } + + #[inline] + pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { + // SAFETY: + // * `buf` and `len` come from a prior decomposition of a valid slice. + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 822b24a9b4691..b3c8815cb062c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -3,18 +3,27 @@ mod borrowed_buf; mod cursor; mod error; +mod io_slice; 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; +#[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}, + util::{chain, take}, +}; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 57ce51bb8c0ed..c98498ef2297a 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/coretests/tests/io/io_slice.rs b/library/coretests/tests/io/io_slice.rs new file mode 100644 index 0000000000000..4639ef2c6ac41 --- /dev/null +++ b/library/coretests/tests/io/io_slice.rs @@ -0,0 +1,100 @@ +use core::io::{IoSlice, IoSliceMut}; +use core::ops::Deref; + +#[test] +fn io_slice_mut_advance_slices() { + let mut buf1 = [1; 8]; + let mut buf2 = [2; 16]; + let mut buf3 = [3; 8]; + let mut bufs = &mut [ + IoSliceMut::new(&mut buf1), + IoSliceMut::new(&mut buf2), + IoSliceMut::new(&mut buf3), + ][..]; + + // Only in a single buffer.. + IoSliceMut::advance_slices(&mut bufs, 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + IoSliceMut::advance_slices(&mut bufs, 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + IoSliceMut::advance_slices(&mut bufs, 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); +} + +#[test] +#[should_panic] +fn io_slice_mut_advance_slices_empty_slice() { + let mut empty_bufs = &mut [][..]; + IoSliceMut::advance_slices(&mut empty_bufs, 1); +} + +#[test] +#[should_panic] +fn io_slice_mut_advance_slices_beyond_total_length() { + let mut buf1 = [1; 8]; + let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; + + IoSliceMut::advance_slices(&mut bufs, 9); + assert!(bufs.is_empty()); +} + +#[test] +fn io_slice_advance_slices() { + let buf1 = [1; 8]; + let buf2 = [2; 16]; + let buf3 = [3; 8]; + let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..]; + + // Only in a single buffer.. + IoSlice::advance_slices(&mut bufs, 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + IoSlice::advance_slices(&mut bufs, 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + IoSlice::advance_slices(&mut bufs, 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); +} + +#[test] +#[should_panic] +fn io_slice_advance_slices_empty_slice() { + let mut empty_bufs = &mut [][..]; + IoSlice::advance_slices(&mut empty_bufs, 1); +} + +#[test] +#[should_panic] +fn io_slice_advance_slices_beyond_total_length() { + let buf1 = [1; 8]; + let mut bufs = &mut [IoSlice::new(&buf1)][..]; + + IoSlice::advance_slices(&mut bufs, 9); + assert!(bufs.is_empty()); +} + +#[test] +fn io_slice_as_slice() { + let buf = [1; 8]; + let slice = IoSlice::new(&buf).as_slice(); + assert_eq!(slice, buf); +} + +#[test] +fn io_slice_into_slice() { + let mut buf = [1; 8]; + let slice = IoSliceMut::new(&mut buf).into_slice(); + assert_eq!(slice, [1; 8]); +} diff --git a/library/coretests/tests/io/mod.rs b/library/coretests/tests/io/mod.rs index a24893a525a9d..7683131a54034 100644 --- a/library/coretests/tests/io/mod.rs +++ b/library/coretests/tests/io/mod.rs @@ -1 +1,2 @@ mod borrowed_buf; +mod io_slice; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 12b81fea9d27c..08a67196d235d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -68,6 +68,7 @@ #![feature(hashmap_internals)] #![feature(int_from_ascii)] #![feature(int_roundings)] +#![feature(io_slice_as_bytes)] #![feature(ip)] #![feature(is_ascii_octdigit)] #![feature(iter_advance_by)] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 360ca83c65a91..6dbe37ca9fba8 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,310 +1,20 @@ #[cfg(test)] mod tests; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::ErrorKind; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use core::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 -// 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(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; - -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 [`std::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`]. -/// -/// [`std::io`]: crate::io -/// [`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`], [`Write`], [`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 -#[stable(feature = "rust1", since = "1.0.0")] -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 -#[allow(dead_code)] -impl Error { - pub(crate) const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - pub(crate) const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - pub(crate) 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 = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - pub(crate) const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - pub(crate) const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - pub(crate) 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`], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// # 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(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 } }, - )) -} - -// 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")] -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) } - } -} - +#[cfg_attr( + test, + expect(unused, reason = "only used in implementation for non-test compilation") +)] +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 { - /// 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)] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self::_new(kind, error.into()) - } - - /// 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`] - /// with [`ErrorKind::Other`]. - /// - /// # 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")] - pub fn other(error: E) -> 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 })) } - } - - /// 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) } - } - /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -324,13 +34,14 @@ 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")] #[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. @@ -358,405 +69,18 @@ impl Error { /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); /// # } /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[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(&error_string(code)), + decode_error_kind, + is_interrupted, + }; - /// 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 - /// it will return [`None`]. - /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::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, - } + // SAFETY: `FUNCTIONS` is a constant and not created at runtime. + unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::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), - } - } - - /// 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 - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::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(&mut *c.error), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`] or [`other`], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// [`other`]: Error::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] - 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), - } - } - - /// 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`]. - /// - /// - /// # 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")] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() - { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { crate::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`] for more details. - /// - /// [`last_os_error`]: Error::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) => sys::io::decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[inline] - pub(crate) fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => sys::io::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", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) - .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 = sys::io::error_string(code); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(ref c) => c.error.fmt(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.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.source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); } 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}")); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 43a6a18ac2c3f..36e61ce68401e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,21 +297,27 @@ #[cfg(test)] mod tests; -#[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; use core::slice::memchr; -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use self::buffered::WriterPanicked; +use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; +pub use alloc_crate::io::RawOsError; #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; +pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; +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, 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; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -328,12 +334,10 @@ 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, take}; -use crate::ops::{Deref, DerefMut}; -use crate::{cmp, fmt, slice, str, sys}; +use crate::mem::MaybeUninit; +use crate::{cmp, fmt, slice, str}; mod buffered; pub(crate) mod copy; @@ -1332,323 +1336,6 @@ pub fn read_to_string(mut reader: R) -> Result { Ok(buf) } -/// A buffer type used with `Read::read_vectored`. -/// -/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[repr(transparent)] -pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSliceMut<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSliceMut<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSliceMut<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSliceMut<'a> { - /// Creates a new `IoSliceMut` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(sys::io::IoSliceMut::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of - /// multiple buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut data = [1; 8]; - /// let mut buf = IoSliceMut::new(&mut data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes, - /// the result will only include the second `IoSliceMut`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), - /// ][..]; - /// - /// // Mark 10 bytes as read. - /// IoSliceMut::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a mutable slice with the original lifetime. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSliceMut; - /// - /// let mut data = *b"abcdef"; - /// let io_slice = IoSliceMut::new(&mut data); - /// io_slice.into_slice()[0] = b'A'; - /// - /// assert_eq!(&data, b"Abcdef"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn into_slice(self) -> &'a mut [u8] { - self.0.into_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSliceMut<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> DerefMut for IoSliceMut<'a> { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - self.0.as_mut_slice() - } -} - -/// A buffer type used with `Write::write_vectored`. -/// -/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a>(sys::io::IoSlice<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSlice<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSlice<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSlice<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSlice<'a> { - /// Creates a new `IoSlice` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[must_use] - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice(sys::io::IoSlice::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple - /// buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let data = [1; 8]; - /// let mut buf = IoSlice::new(&data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSlice`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes, - /// the result will only include the second `IoSlice`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let buf1 = [1; 8]; - /// let buf2 = [2; 16]; - /// let buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSlice::new(&buf1), - /// IoSlice::new(&buf2), - /// IoSlice::new(&buf3), - /// ][..]; - /// - /// // Mark 10 bytes as written. - /// IoSlice::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. This prevents overflow - // that could happen if the length of slices in `bufs` were instead - // accumulated. Those slice may be aliased and, if they are large - // enough, their added length may overflow a `usize`. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a slice with the original lifetime. - /// - /// This doesn't borrow from `self`, so is less restrictive than calling - /// `.deref()`, which does. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSlice; - /// - /// let data = b"abcdef"; - /// - /// let mut io_slice = IoSlice::new(data); - /// let tail = &io_slice.as_slice()[3..]; - /// - /// // This works because `tail` doesn't borrow `io_slice` - /// io_slice = IoSlice::new(tail); - /// - /// assert_eq!(io_slice.as_slice(), b"def"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn as_slice(self) -> &'a [u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSlice<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 12cbb1d50233c..56d3bd678e104 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,10 +1,7 @@ use super::{BorrowedBuf, Cursor, SeekFrom, repeat}; use crate::cmp::{self, min}; -use crate::io::{ - self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write, -}; +use crate::io::{self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, Write}; use crate::mem::MaybeUninit; -use crate::ops::Deref; #[test] fn read_until() { @@ -565,104 +562,6 @@ fn test_read_to_end_capacity() -> io::Result<()> { Ok(()) } -#[test] -fn io_slice_mut_advance_slices() { - let mut buf1 = [1; 8]; - let mut buf2 = [2; 16]; - let mut buf3 = [3; 8]; - let mut bufs = &mut [ - IoSliceMut::new(&mut buf1), - IoSliceMut::new(&mut buf2), - IoSliceMut::new(&mut buf3), - ][..]; - - // Only in a single buffer.. - IoSliceMut::advance_slices(&mut bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - IoSliceMut::advance_slices(&mut bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - IoSliceMut::advance_slices(&mut bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); -} - -#[test] -#[should_panic] -fn io_slice_mut_advance_slices_empty_slice() { - let mut empty_bufs = &mut [][..]; - IoSliceMut::advance_slices(&mut empty_bufs, 1); -} - -#[test] -#[should_panic] -fn io_slice_mut_advance_slices_beyond_total_length() { - let mut buf1 = [1; 8]; - let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; - - IoSliceMut::advance_slices(&mut bufs, 9); - assert!(bufs.is_empty()); -} - -#[test] -fn io_slice_advance_slices() { - let buf1 = [1; 8]; - let buf2 = [2; 16]; - let buf3 = [3; 8]; - let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..]; - - // Only in a single buffer.. - IoSlice::advance_slices(&mut bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - IoSlice::advance_slices(&mut bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - IoSlice::advance_slices(&mut bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); -} - -#[test] -#[should_panic] -fn io_slice_advance_slices_empty_slice() { - let mut empty_bufs = &mut [][..]; - IoSlice::advance_slices(&mut empty_bufs, 1); -} - -#[test] -#[should_panic] -fn io_slice_advance_slices_beyond_total_length() { - let buf1 = [1; 8]; - let mut bufs = &mut [IoSlice::new(&buf1)][..]; - - IoSlice::advance_slices(&mut bufs, 9); - assert!(bufs.is_empty()); -} - -#[test] -fn io_slice_as_slice() { - let buf = [1; 8]; - let slice = IoSlice::new(&buf).as_slice(); - assert_eq!(slice, buf); -} - -#[test] -fn io_slice_into_slice() { - let mut buf = [1; 8]; - let slice = IoSliceMut::new(&mut buf).into_slice(); - assert_eq!(slice, [1; 8]); -} - /// Creates a new writer that reads from at most `n_bufs` and reads /// `per_call` bytes (in total) per call to write. fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7a548dfb33da7..e16888fd490ad 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -382,6 +382,7 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index a739d6ad2a90d..5f1f555d5f6ba 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 diff --git a/library/std/src/sys/io/io_slice/windows.rs b/library/std/src/sys/io/io_slice/windows.rs deleted file mode 100644 index c3d8ec87c19e3..0000000000000 --- a/library/std/src/sys/io/io_slice/windows.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::marker::PhantomData; -use crate::slice; -use crate::sys::c; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 445bcdef0aa1f..0158137174087 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -2,27 +2,6 @@ mod error; -mod io_slice { - cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { - mod iovec; - pub use iovec::*; - } - target_os = "windows" => { - mod windows; - pub use windows::*; - } - target_os = "uefi" => { - mod uefi; - pub use uefi::*; - } - _ => { - mod unsupported; - pub use unsupported::*; - } - } -} - mod is_terminal { cfg_select! { any(target_family = "unix", target_os = "wasi") => { @@ -63,7 +42,6 @@ pub use error::errno_location; ))] pub use error::set_errno; pub use error::{decode_error_kind, errno, error_string, is_interrupted}; -pub use io_slice::{IoSlice, IoSliceMut}; pub use is_terminal::is_terminal; pub use kernel_copy::{CopyState, kernel_copy}; diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e07a0784cdb3a..e7455fc701cfb 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -78,6 +78,20 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ ("core/primitive.slice.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase", "core/slice::sort_by_key", "core\\slice::sort_by_key", "#method.sort_by_cached_key"]), + ("core/io/struct.IoSlice.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "core/io/slice::sort_by_key", + "core\\io\\slice::sort_by_key", + "#method.sort_by_cached_key" + ]), + ("core/io/struct.IoSliceMut.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "core/io/slice::sort_by_key", + "core\\io\\slice::sort_by_key", + "#method.sort_by_cached_key" + ]), ]; #[rustfmt::skip] 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 73d8272c3c263f1cd675ff3fe8c6cf83122b4755 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 21:20:35 +1000 Subject: [PATCH 02/15] Move `std::io::IoHandle` to `core::io` --- library/alloc/src/io/mod.rs | 2 +- library/core/src/io/mod.rs | 18 ++++++++++++++++++ library/std/src/fs.rs | 2 ++ library/std/src/io/mod.rs | 16 +--------------- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 327701c542494..b2ca1d5f09fad 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -18,4 +18,4 @@ pub use core::io::{ use core::io::{Custom, CustomOwner}; #[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..5726e4787169f 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -27,3 +27,21 @@ 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 +#[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 3702850a2b136..1c748d235639b 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 923b8a3fd386f27fb440f425e313842dd37ab202 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 21:50:01 +1000 Subject: [PATCH 03/15] 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 b2ca1d5f09fad..165ff6b7d9077 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; @@ -18,4 +19,4 @@ pub use core::io::{ use core::io::{Custom, CustomOwner}; #[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 5726e4787169f..ba201b08fa8f2 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 ac461f4f0307e..4f220e8f7d8c0 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -579,6 +579,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 e1a223ac2c4aa12943225daf054de9e69e31cc74 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 09:03:11 +1000 Subject: [PATCH 04/15] Move `std::io::Seek` to `core::io` --- library/alloc/src/io/impls.rs | 63 +++++++- 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 | 222 ++++++++++++++++++++++++++++ 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 | 266 +--------------------------------- library/std/src/io/util.rs | 21 +-- library/std/src/lib.rs | 1 + 13 files changed, 425 insertions(+), 412 deletions(-) create mode 100644 library/core/src/io/seek.rs diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 6f392bba93ef4..dd78e9aa47d65 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,5 +1,5 @@ use crate::boxed::Box; -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; // ============================================================================= // Forwarding implementations @@ -18,5 +18,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) + } +} + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for crate::sync::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) + } +} + // ============================================================================= // In-memory buffer implementations diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 165ff6b7d9077..6d1ee3c01a0f0 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,10 +13,10 @@ 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, Seek, SeekFrom, + Sink, Take, empty, repeat, sink, }; use core::io::{Custom, CustomOwner}; #[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 ea333c8d84574..44e44d412d670 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -149,6 +149,7 @@ #![feature(ptr_metadata)] #![feature(raw_os_error_ty)] #![feature(rev_into_inner)] +#![feature(seek_stream_len)] #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] 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 ba201b08fa8f2..a1f5d307d61a3 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,12 +22,14 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, + 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 new file mode 100644 index 0000000000000..eda592de6b812 --- /dev/null +++ b/library/core/src/io/seek.rs @@ -0,0 +1,222 @@ +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`: +/// +/// [`File`]: ../../std/fs/struct.File.html +/// +/// ```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`]: ../../std/io/struct.BufReader.html + #[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. +#[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/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 84014cc245c2a..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, 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,225 +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) -} - -/// 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 { @@ -2566,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 e16888fd490ad..8de5e8f1eb658 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -367,6 +367,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(seek_stream_len)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] From f4769643a1f9c5fbea06b903cd412c87f7298e72 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 22:36:50 +1000 Subject: [PATCH 05/15] 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 | 107 ++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 1630b2068f5aa..468eaac1a5899 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -102,6 +102,47 @@ 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<()>; +} + +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 +354,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 ee5af0cee9a87441d28d1a3b5389f5977153a03f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 22:39:47 +1000 Subject: [PATCH 06/15] Move `std::io::Write` to `core::io` --- library/alloc/src/io/cursor.rs | 249 ++++++++++++++++++++ library/alloc/src/io/impls.rs | 170 +++++++++++++- library/alloc/src/io/mod.rs | 9 +- library/alloc/src/lib.rs | 2 + library/core/src/io/cursor.rs | 176 +++++++++++++- library/core/src/io/impls.rs | 147 +++++++++++- library/core/src/io/mod.rs | 7 + library/core/src/io/util.rs | 158 ++++++++++++- library/core/src/io/write.rs | 411 ++++++++++++++++++++++++++++++++ library/std/src/io/cursor.rs | 408 +------------------------------- library/std/src/io/impls.rs | 311 +----------------------- library/std/src/io/mod.rs | 415 +-------------------------------- library/std/src/io/util.rs | 161 +------------ library/std/src/lib.rs | 2 + 14 files changed, 1333 insertions(+), 1293 deletions(-) create mode 100644 library/alloc/src/io/cursor.rs create mode 100644 library/core/src/io/write.rs diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs new file mode 100644 index 0000000000000..187ee0ab88381 --- /dev/null +++ b/library/alloc/src/io/cursor.rs @@ -0,0 +1,249 @@ +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 + 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 = "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 dd78e9aa47d65..3f300ec30977a 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,5 +1,9 @@ +use crate::alloc::Allocator; use crate::boxed::Box; -use crate::io::{self, Seek, SeekFrom, SizeHint}; +use crate::collections::VecDeque; +use crate::fmt; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::vec::Vec; // ============================================================================= // Forwarding implementations @@ -18,6 +22,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] @@ -46,6 +87,48 @@ impl Seek for Box { } } +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for crate::sync::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 crate::sync::Arc @@ -81,3 +164,88 @@ where // ============================================================================= // 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 { + 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(()) + } +} + +/// 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(()) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 6d1ee3c01a0f0..ce064975d134e 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,9 +15,13 @@ 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, }; use core::io::{Custom, CustomOwner}; #[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, 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 44e44d412d670..c1e71ce6125e2 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)] @@ -173,6 +174,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 ec09dba72606d..1fef1f85513e4 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,4 +1,5 @@ -use crate::io::{self, ErrorKind, SeekFrom}; +use crate::cmp; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -292,6 +293,135 @@ 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 amt = (&mut slice[(pos as usize)..]).write(buf)?; + *pos_mut += amt as u64; + 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) +} + +#[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(()) } +} + +#[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 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 @@ -326,3 +456,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 a1f5d307d61a3..28b9d43b79eb5 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}; @@ -24,14 +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::{ + 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_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 new file mode 100644 index 0000000000000..282b3322ef02a --- /dev/null +++ b/library/core/src/io/write.rs @@ -0,0 +1,411 @@ +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`]: 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 + } +} + +#[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) +} + +#[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 468eaac1a5899..36e76b2b68a1b 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,10 +4,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use crate::alloc::Allocator; -use crate::cmp; 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 @@ -101,407 +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<()>; -} - -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 { - 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, - 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)?; - if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } -} - -#[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, - 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 93f97bffdfe4b..75e85a9659e78 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,9 +310,10 @@ 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, 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}; @@ -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; @@ -549,14 +550,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) { @@ -600,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'. @@ -1337,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 8de5e8f1eb658..c098ecb293208 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -315,6 +315,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -379,6 +380,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(write_all_vectored)] // tidy-alphabetical-end // // Library features (alloc): From 1d1c65749a24db426669cf9b540dc39c6b695ba1 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 13:20:47 +1000 Subject: [PATCH 07/15] Move `std::io::Read` to `alloc::io` --- library/alloc/src/io/cursor.rs | 85 +- library/alloc/src/io/impls.rs | 340 ++++++- library/alloc/src/io/mod.rs | 14 + library/alloc/src/io/read.rs | 897 ++++++++++++++++++ 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 | 1081 +--------------------- library/std/src/io/stdio.rs | 2 + library/std/src/io/util.rs | 99 +- library/std/src/lib.rs | 1 + library/std/src/sys/io/mod.rs | 4 - library/std/src/sys/stdio/unix.rs | 2 +- 15 files changed, 1670 insertions(+), 1599 deletions(-) create mode 100644 library/alloc/src/io/read.rs 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 187ee0ab88381..8af74e79fd1ee 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -1,11 +1,92 @@ 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)?; + 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) + } +} + /// 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 3f300ec30977a..65d705304b0d4 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,13 +1,101 @@ +use core::cmp; + use crate::alloc::Allocator; use crate::boxed::Box; 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; 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 { @@ -87,6 +175,54 @@ impl Seek for Box { } } +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Read for crate::sync::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 crate::sync::Arc @@ -165,6 +301,118 @@ where // ============================================================================= // 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) + } +} + /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] @@ -208,6 +456,96 @@ impl Write for Vec { } } +/// 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)) } + } +} /// 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 { diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index ce064975d134e..72c264701886a 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -3,6 +3,8 @@ mod cursor; mod error; mod impls; +mod read; +mod util; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -25,3 +27,15 @@ pub use core::io::{ default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, 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_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 new file mode 100644 index 0000000000000..16c68df0a4426 --- /dev/null +++ b/library/alloc/src/io/read.rs @@ -0,0 +1,897 @@ +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`]: 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(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, + { + 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, + { + 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) }) + } +} + +// 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 = if cfg!(target_os = "espidf") { 512 } else { 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 + } +} + +// 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 { + 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); + } + } + } +} + +#[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 +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_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<()> +where + F: FnOnce(&mut [u8]) -> Result, +{ + let n = read(cursor.ensure_init())?; + 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 c1e71ce6125e2..d689e0d0eebfb 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)] @@ -116,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)] @@ -141,6 +143,8 @@ #![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)] @@ -210,6 +214,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 4f220e8f7d8c0..da1a7389f957a 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -317,6 +317,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 75e85a9659e78..0a6f1d7341b19 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,10 +310,14 @@ 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, +}; +pub(crate) use alloc_crate::io::{ + DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, 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_write_vectored, stream_len_default, uninlined_slow_read_byte, }; -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}; @@ -338,8 +342,7 @@ pub use self::{ cursor::Cursor, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -use crate::mem::MaybeUninit; -use crate::{cmp, slice, str}; +use crate::cmp; mod buffered; pub(crate) mod copy; @@ -351,879 +354,8 @@ 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> { - 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 -// - 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_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) { - 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(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<'_>, -) -> 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 @@ -1752,65 +884,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]> { @@ -1846,79 +919,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]> { @@ -1940,71 +940,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 2d80fe49e80a7..02b5e8ab7c0ba 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -543,6 +543,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 c098ecb293208..002bf6cdcac47 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -390,6 +390,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/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 0158137174087..41fa786ad895b 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -44,7 +44,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 }; diff --git a/library/std/src/sys/stdio/unix.rs b/library/std/src/sys/stdio/unix.rs index ffed376a8e96e..d61eadc41cb39 100644 --- a/library/std/src/sys/stdio/unix.rs +++ b/library/std/src/sys/stdio/unix.rs @@ -93,7 +93,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) From 5f8e56afd645310c4d1e8ab090ed8739363454b9 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 13:24:27 +1000 Subject: [PATCH 08/15] 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 72c264701886a..2cca8b8525ca8 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -28,8 +28,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::{ @@ -39,3 +37,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 16c68df0a4426..7a575e74d9337 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -638,6 +638,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 0a6f1d7341b19..6d9ec3dd843f9 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")] @@ -356,71 +358,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 c28673c9144e5b086306f24c449f871091668ca7 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 13:55:43 +1000 Subject: [PATCH 09/15] Move `std::io::BufRead` to `alloc::io` --- library/alloc/src/io/buf_read.rs | 468 ++++++++++++++++++++++++ library/alloc/src/io/cursor.rs | 18 +- library/alloc/src/io/impls.rs | 97 ++++- library/alloc/src/io/mod.rs | 14 +- library/alloc/src/io/util.rs | 172 ++++++++- library/std/src/io/cursor.rs | 20 -- library/std/src/io/impls.rs | 109 ------ library/std/src/io/mod.rs | 598 +------------------------------ library/std/src/io/util.rs | 35 -- 9 files changed, 760 insertions(+), 771 deletions(-) create mode 100644 library/alloc/src/io/buf_read.rs delete mode 100644 library/std/src/io/cursor.rs delete mode 100644 library/std/src/io/impls.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..9663acd00fee7 --- /dev/null +++ b/library/alloc/src/io/buf_read.rs @@ -0,0 +1,468 @@ +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][`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(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]: 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(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), + }; + 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); + } + } +} diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index 8af74e79fd1ee..27079d8f31027 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; @@ -87,6 +88,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 65d705304b0d4..047934aabf2b0 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -4,7 +4,9 @@ use crate::alloc::Allocator; use crate::boxed::Box; 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; use crate::vec::Vec; @@ -53,6 +55,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 { @@ -96,6 +130,38 @@ impl Read for Box { (**self).read_buf_exact(cursor) } } +#[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) + } +} #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SizeHint for Box { @@ -412,6 +478,18 @@ impl Read for &[u8] { Ok(len) } } +#[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. @@ -546,6 +624,23 @@ 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`. +#[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. #[stable(feature = "vecdeque_read_write", since = "1.63.0")] impl Write for VecDeque { diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 2cca8b8525ca8..dfadf8b21ca8f 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; @@ -28,6 +29,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::{ @@ -35,10 +42,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..e1df6e9fdd1b2 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 deleted file mode 100644 index 5c7b7a266d2fc..0000000000000 --- a/library/std/src/io/cursor.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[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 deleted file mode 100644 index 7329b56b2fb3f..0000000000000 --- a/library/std/src/io/impls.rs +++ /dev/null @@ -1,109 +0,0 @@ -#[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 6d9ec3dd843f9..8cb5c75d4fe75 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, }; pub(crate) use alloc_crate::io::{ DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, append_to_string, default_read_buf, @@ -341,607 +339,15 @@ 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; -mod cursor; mod error; -mod impls; mod pipe; pub mod prelude; 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) - } -} From 0bbdac25bb107b445c144dd57415c010ea7cbcba Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 14:10:14 +1000 Subject: [PATCH 10/15] Move `std::io::buffered` to `alloc::io` --- .../src/io/buffered/bufreader.rs | 18 +- .../src/io/buffered/bufreader/buffer.rs | 19 +- .../src/io/buffered/bufwriter.rs | 22 +- .../src/io/buffered/linewriter.rs | 0 .../src/io/buffered/linewritershim.rs | 1 + library/alloc/src/io/buffered/mod.rs | 191 ++++++++++++++++++ library/alloc/src/io/mod.rs | 3 + library/std/src/io/buffered/mod.rs | 185 ----------------- library/std/src/io/mod.rs | 17 +- 9 files changed, 250 insertions(+), 206 deletions(-) rename library/{std => alloc}/src/io/buffered/bufreader.rs (96%) rename library/{std => alloc}/src/io/buffered/bufreader/buffer.rs (91%) rename library/{std => alloc}/src/io/buffered/bufwriter.rs (97%) rename library/{std => alloc}/src/io/buffered/linewriter.rs (100%) 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 96% rename from library/std/src/io/buffered/bufreader.rs rename to library/alloc/src/io/buffered/bufreader.rs index da1a7389f957a..2372e70357c19 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -1,12 +1,16 @@ mod buffer; -use buffer::Buffer; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub 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. /// @@ -74,11 +78,15 @@ impl 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 } } @@ -279,7 +287,9 @@ 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() } } diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/alloc/src/io/buffered/bufreader/buffer.rs similarity index 91% rename from library/std/src/io/buffered/bufreader/buffer.rs rename to library/alloc/src/io/buffered/bufreader/buffer.rs index f2efcc5ab8601..3e158ffb71c1e 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/alloc/src/io/buffered/bufreader/buffer.rs @@ -9,10 +9,14 @@ //! 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_docs)] +#[expect(missing_debug_implementations)] pub struct Buffer { // The buffer. buf: Box<[MaybeUninit]>, @@ -29,12 +33,15 @@ pub struct Buffer { } impl Buffer { + #[expect(missing_docs)] #[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) { @@ -45,6 +52,7 @@ impl Buffer { } } + #[expect(missing_docs)] #[inline] pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and @@ -52,16 +60,19 @@ impl Buffer { unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } + #[expect(missing_docs)] #[inline] pub fn capacity(&self) -> usize { self.buf.len() } + #[expect(missing_docs)] #[inline] pub fn filled(&self) -> usize { self.filled } + #[expect(missing_docs)] #[inline] pub fn pos(&self) -> usize { self.pos @@ -73,12 +84,14 @@ impl Buffer { self.initialized } + #[expect(missing_docs)] #[inline] pub fn discard_buffer(&mut self) { self.pos = 0; self.filled = 0; } + #[expect(missing_docs)] #[inline] pub fn consume(&mut self, amt: usize) { self.pos = cmp::min(self.pos + amt, self.filled); @@ -101,6 +114,7 @@ impl Buffer { } } + #[expect(missing_docs)] #[inline] pub fn unconsume(&mut self, amt: usize) { self.pos = self.pos.saturating_sub(amt); @@ -130,6 +144,7 @@ impl Buffer { self.pos = 0; } + #[expect(missing_docs)] #[inline] pub 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 diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs similarity index 97% rename from library/std/src/io/buffered/bufwriter.rs rename to library/alloc/src/io/buffered/bufwriter.rs index 1a5cc911c0e43..cd87a93413c76 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. /// @@ -94,13 +96,17 @@ impl 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 } } @@ -192,7 +198,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. @@ -343,7 +351,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 100% rename from library/std/src/io/buffered/linewriter.rs rename to library/alloc/src/io/buffered/linewriter.rs 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..d1e6b99f370d1 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/alloc/src/io/buffered/linewritershim.rs @@ -18,6 +18,7 @@ pub struct LineWriterShim<'a, W: ?Sized + Write> { } impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { + #[expect(missing_docs)] pub 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..96213174a23bc --- /dev/null +++ b/library/alloc/src/io/buffered/mod.rs @@ -0,0 +1,191 @@ +//! Buffering wrappers for I/O traits + +mod bufreader; +mod bufwriter; +mod linewriter; +mod linewritershim; + +use core::{error, fmt}; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::bufreader::Buffer; +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use self::bufwriter::WriterPanicked; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub 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; +/// +/// 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/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index dfadf8b21ca8f..4b6f541808038 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; @@ -32,12 +33,14 @@ 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}, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + buffered::{Buffer, LineWriterShim}, 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/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 8cb5c75d4fe75..b40afe205f57b 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,20 +313,17 @@ 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, }; pub(crate) use alloc_crate::io::{ - DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, 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_write_vectored, stream_len_default, uninlined_slow_read_byte, + DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, default_read_buf, 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")] @@ -337,7 +337,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}, }; From 74d6ee74ca9fa035ac3dd7ac7c43c3516aaf1ef4 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 15:30:58 +1000 Subject: [PATCH 11/15] Move `std::io::copy` to `alloc::io` --- library/alloc/src/io/copy.rs | 234 +++++++++++++++++++++++++++++++++++ library/alloc/src/io/mod.rs | 2 + library/std/src/io/copy.rs | 230 +--------------------------------- library/std/src/io/mod.rs | 9 +- 4 files changed, 241 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..96d75654a5efd --- /dev/null +++ b/library/alloc/src/io/copy.rs @@ -0,0 +1,234 @@ +use core::cmp; +use core::mem::MaybeUninit; + +use crate::alloc::Allocator; +use crate::collections::VecDeque; +use crate::io::{ + BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, IoSlice, Read, Result, Write, +}; +use crate::vec::Vec; + +/// 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) + } +} + +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 4b6f541808038..16e0adb0d79bb 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; @@ -41,6 +42,7 @@ pub use self::{ #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ buffered::{Buffer, LineWriterShim}, + 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 b40afe205f57b..66f0fdb161667 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -319,7 +319,8 @@ pub use alloc_crate::io::{ }; pub(crate) use alloc_crate::io::{ DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, default_read_buf, default_read_to_end, - default_read_to_string, default_read_vectored, default_write_vectored, stream_len_default, + default_read_to_string, default_read_vectored, default_write_vectored, generic_copy, + stream_len_default, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -328,10 +329,10 @@ pub use alloc_crate::io::{IoSlice, IoSliceMut}; pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; -pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "print_internals", issue = "none")] #[doc(hidden)] pub use self::stdio::{_eprint, _print}; +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}; @@ -342,11 +343,9 @@ pub use self::{ }; mod buffered; -pub(crate) mod copy; +mod copy; mod error; mod pipe; pub mod prelude; mod stdio; mod util; - -pub(crate) use stdio::cleanup; From 8566cbbaa88b7125c304738d0941c2df83e25333 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 13 May 2026 16:41:18 +1000 Subject: [PATCH 12/15] WIP: Trick tidy by using cfg_select --- library/alloc/src/io/read.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 7a575e74d9337..fc7150642fe3b 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -707,7 +707,10 @@ pub fn read_to_string(mut reader: R) -> Result { // (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 = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; +pub const DEFAULT_BUF_SIZE: usize = cfg_select! { + target_os = "espidf" => { 512 }, + _ => { 8 * 1024 } +}; struct Guard<'a> { buf: &'a mut Vec, From d84260c141fc2d51a6f7ba1f3293b6d414d0b1b9 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 14 May 2026 08:47:15 +1000 Subject: [PATCH 13/15] WIP: Fix usage of DEFAULT_BUF_SIZE --- library/std/src/io/mod.rs | 5 ++--- library/std/src/sys/stdio/motor.rs | 2 +- library/std/src/sys/stdio/sgx.rs | 2 +- library/std/src/sys/stdio/unix.rs | 2 +- library/std/src/sys/stdio/zkvm.rs | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 66f0fdb161667..a608db5777cc2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -318,9 +318,8 @@ pub use alloc_crate::io::{ repeat, sink, }; pub(crate) use alloc_crate::io::{ - DEFAULT_BUF_SIZE, IoHandle, SpecReadByte, default_read_buf, default_read_to_end, - default_read_to_string, default_read_vectored, default_write_vectored, generic_copy, - stream_len_default, + IoHandle, SpecReadByte, default_read_buf, default_read_to_end, default_read_to_string, + default_read_vectored, default_write_vectored, generic_copy, stream_len_default, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/stdio/motor.rs b/library/std/src/sys/stdio/motor.rs index 2b1bc15285f9c..54510a9e1c892 100644 --- a/library/std/src/sys/stdio/motor.rs +++ b/library/std/src/sys/stdio/motor.rs @@ -2,7 +2,7 @@ use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, Ra use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; use crate::{io, process, sys}; -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = alloc_crate::io::DEFAULT_BUF_SIZE; pub struct Stdin {} diff --git a/library/std/src/sys/stdio/sgx.rs b/library/std/src/sys/stdio/sgx.rs index 2cf47f4919209..821896e040690 100644 --- a/library/std/src/sys/stdio/sgx.rs +++ b/library/std/src/sys/stdio/sgx.rs @@ -89,7 +89,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = alloc_crate::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` diff --git a/library/std/src/sys/stdio/unix.rs b/library/std/src/sys/stdio/unix.rs index d61eadc41cb39..73f736e035fdc 100644 --- a/library/std/src/sys/stdio/unix.rs +++ b/library/std/src/sys/stdio/unix.rs @@ -93,7 +93,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = alloc_crate::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) diff --git a/library/std/src/sys/stdio/zkvm.rs b/library/std/src/sys/stdio/zkvm.rs index 84496ac937363..c548f84c73fa4 100644 --- a/library/std/src/sys/stdio/zkvm.rs +++ b/library/std/src/sys/stdio/zkvm.rs @@ -61,7 +61,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = alloc_crate::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true From 066b445810e0ad99600c5424a01b5064ea3db8e4 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 14 May 2026 08:47:34 +1000 Subject: [PATCH 14/15] WIP: Move `std::io` tests into `alloctests` --- library/alloc/src/io/buffered/bufreader.rs | 3 ++- .../alloc/src/io/buffered/bufreader/buffer.rs | 3 ++- library/alloc/src/io/buffered/bufwriter.rs | 4 ++++ library/alloc/src/io/buffered/mod.rs | 3 +++ library/alloc/src/io/read.rs | 1 + .../tests/io/buffered.rs} | 24 +++++++++++-------- .../tests/io/cursor.rs} | 5 ++-- .../tests.rs => alloctests/tests/io/mod.rs} | 20 +++++++++++----- .../tests.rs => alloctests/tests/io/util.rs} | 10 ++++---- library/alloctests/tests/lib.rs | 13 ++++++++++ library/std/src/io/buffered/mod.rs | 4 ---- library/std/src/io/copy/tests.rs | 2 ++ library/std/src/io/mod.rs | 5 ---- library/std/src/io/util.rs | 2 -- 14 files changed, 63 insertions(+), 36 deletions(-) rename library/{std/src/io/buffered/tests.rs => alloctests/tests/io/buffered.rs} (98%) rename library/{std/src/io/cursor/tests.rs => alloctests/tests/io/cursor.rs} (99%) rename library/{std/src/io/tests.rs => alloctests/tests/io/mod.rs} (98%) rename library/{std/src/io/util/tests.rs => alloctests/tests/io/util.rs} (96%) delete mode 100644 library/std/src/io/buffered/mod.rs delete mode 100644 library/std/src/io/util.rs diff --git a/library/alloc/src/io/buffered/bufreader.rs b/library/alloc/src/io/buffered/bufreader.rs index 2372e70357c19..ed917431634a0 100644 --- a/library/alloc/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -295,8 +295,9 @@ impl BufReader { } // 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() diff --git a/library/alloc/src/io/buffered/bufreader/buffer.rs b/library/alloc/src/io/buffered/bufreader/buffer.rs index 3e158ffb71c1e..ba3a24179c5d1 100644 --- a/library/alloc/src/io/buffered/bufreader/buffer.rs +++ b/library/alloc/src/io/buffered/bufreader/buffer.rs @@ -79,7 +79,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/alloc/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs index cd87a93413c76..856336d75491a 100644 --- a/library/alloc/src/io/buffered/bufwriter.rs +++ b/library/alloc/src/io/buffered/bufwriter.rs @@ -89,6 +89,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()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -121,6 +122,7 @@ 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); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -142,6 +144,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 @@ -295,6 +298,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 diff --git a/library/alloc/src/io/buffered/mod.rs b/library/alloc/src/io/buffered/mod.rs index 96213174a23bc..d4c4c1f008696 100644 --- a/library/alloc/src/io/buffered/mod.rs +++ b/library/alloc/src/io/buffered/mod.rs @@ -29,6 +29,7 @@ use crate::io::Error; /// 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 @@ -71,6 +72,7 @@ impl IntoInnerError { /// 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 @@ -105,6 +107,7 @@ impl IntoInnerError { /// 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 diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index fc7150642fe3b..a8c29c1acef26 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -248,6 +248,7 @@ pub trait Read { /// 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] { &[] } 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..753c645ad7fd2 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 { @@ -681,7 +685,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 +1027,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 +1060,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 +1083,7 @@ fn bufreader_full_initialize() { /// This is a regression test for https://github.com/rust-lang/rust/issues/127584. #[test] fn bufwriter_aliasing() { - use crate::io::{BufWriter, Cursor}; + use alloc::io::{BufWriter, Cursor}; let mut v = vec![0; 1024]; let c = Cursor::new(&mut v); let w = BufWriter::new(Box::new(c)); diff --git a/library/std/src/io/cursor/tests.rs b/library/alloctests/tests/io/cursor.rs similarity index 99% rename from library/std/src/io/cursor/tests.rs rename to library/alloctests/tests/io/cursor.rs index d7c203c297fe6..5e863f29af53c 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/alloctests/tests/io/cursor.rs @@ -1,5 +1,6 @@ -use crate::io::prelude::*; -use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom}; +use alloc::io::{Cursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; + +extern crate test; #[test] fn test_vec_writer() { diff --git a/library/std/src/io/tests.rs b/library/alloctests/tests/io/mod.rs similarity index 98% rename from library/std/src/io/tests.rs rename to library/alloctests/tests/io/mod.rs index 56d3bd678e104..8b5a32e523644 100644 --- a/library/std/src/io/tests.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,7 +1,15 @@ -use super::{BorrowedBuf, Cursor, SeekFrom, repeat}; -use crate::cmp::{self, min}; -use crate::io::{self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, Write}; -use crate::mem::MaybeUninit; +mod buffered; +mod cursor; +mod util; + +use alloc::io::{ + self, BorrowedBuf, BufRead, BufReader, Cursor, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, SeekFrom, + Write, repeat, +}; +use core::cmp::{self, min}; +use core::mem::MaybeUninit; + +extern crate test; #[test] fn read_until() { @@ -270,7 +278,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 +368,7 @@ fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { let mut lr = repeat(1).take(10000000); let mut vec = Vec::with_capacity(1024); - super::default_read_to_end(&mut lr, &mut vec, None) + alloc::io::default_read_to_end(&mut lr, &mut vec, None) }); } diff --git a/library/std/src/io/util/tests.rs b/library/alloctests/tests/io/util.rs similarity index 96% rename from library/std/src/io/util/tests.rs rename to library/alloctests/tests/io/util.rs index 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 699a5010282b0..bfec2170bb7bf 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -41,6 +41,18 @@ #![feature(macro_metavar_expr_concat)] #![feature(vec_peek_mut)] #![feature(vec_try_remove)] +#![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)] +#![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)] @@ -62,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/buffered/mod.rs b/library/std/src/io/buffered/mod.rs deleted file mode 100644 index 1d09ff7d8dc1c..0000000000000 --- a/library/std/src/io/buffered/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Buffering wrappers for I/O traits - -#[cfg(test)] -mod tests; diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 7bdba3a04416e..3136e4a2fbce9 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -1,3 +1,5 @@ +use alloc_crate::io::DEFAULT_BUF_SIZE; + use crate::cmp::{max, min}; use crate::collections::VecDeque; use crate::io; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index a608db5777cc2..b0611788b3173 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -294,9 +294,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; @@ -341,10 +338,8 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -mod buffered; mod copy; mod error; 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 34a887d3f44cac93ad4d024c4ee4ac0040f55347 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 15 May 2026 13:57:16 +1000 Subject: [PATCH 15/15] WIP: Remove broken documentation links --- library/alloc/src/io/buf_read.rs | 16 +++- library/alloc/src/io/buffered/bufreader.rs | 5 +- library/alloc/src/io/buffered/bufwriter.rs | 6 +- .../alloc/src/io/buffered/linewritershim.rs | 3 +- library/alloc/src/io/read.rs | 80 +++++++++---------- library/core/src/io/seek.rs | 8 +- library/core/src/io/write.rs | 8 +- src/tools/linkchecker/main.rs | 16 ++++ 8 files changed, 82 insertions(+), 60 deletions(-) diff --git a/library/alloc/src/io/buf_read.rs b/library/alloc/src/io/buf_read.rs index 9663acd00fee7..9c8f6bc027ffb 100644 --- a/library/alloc/src/io/buf_read.rs +++ b/library/alloc/src/io/buf_read.rs @@ -29,10 +29,10 @@ use crate::vec::Vec; /// 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`. +/// For example, `File` implements [`Read`], but not `BufRead`. /// [`BufReader`] to the rescue! /// -/// [`File`]: crate::fs::File +/// [`BufReader`]: crate::io::BufReader /// [`read_line`]: BufRead::read_line /// [`lines`]: BufRead::lines /// @@ -174,6 +174,8 @@ pub trait BufRead: Read { /// 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}; /// @@ -236,6 +238,8 @@ pub trait BufRead: Read { /// 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}; /// @@ -307,6 +311,8 @@ pub trait BufRead: Read { /// [`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}; /// @@ -360,6 +366,8 @@ pub trait BufRead: Read { /// 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}; /// @@ -385,7 +393,7 @@ pub trait BufRead: Read { /// [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" + /// [io::Result]: crate::io::Result "io::Result" /// /// # Examples /// @@ -393,6 +401,8 @@ pub trait BufRead: Read { /// this example, we use [`Cursor`] to iterate over all the lines in a byte /// slice. /// + /// [`Cursor`]: crate::io::Cursor + /// /// ``` /// use std::io::{self, BufRead}; /// diff --git a/library/alloc/src/io/buffered/bufreader.rs b/library/alloc/src/io/buffered/bufreader.rs index ed917431634a0..2a60934b9f2f7 100644 --- a/library/alloc/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -15,7 +15,7 @@ use crate::vec::Vec; /// The `BufReader` struct adds buffering to any reader. /// /// It can be excessively inefficient to work directly with a [`Read`] instance. -/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] +/// For example, every call to `read` on `TcpStream` /// results in a system call. A `BufReader` performs large, infrequent reads on /// the underlying [`Read`] and maintains an in-memory buffer of the results. /// @@ -31,9 +31,6 @@ use crate::vec::Vec; /// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause /// data loss. /// -/// [`TcpStream::read`]: crate::net::TcpStream::read -/// [`TcpStream`]: crate::net::TcpStream -/// /// # Examples /// /// ```no_run diff --git a/library/alloc/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs index 856336d75491a..9b47c31689182 100644 --- a/library/alloc/src/io/buffered/bufwriter.rs +++ b/library/alloc/src/io/buffered/bufwriter.rs @@ -10,7 +10,7 @@ use crate::vec::Vec; /// /// It can be excessively inefficient to work directly with something that /// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A +/// `write` on `TcpStream` results in a system call. A /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying /// writer in large, infrequent batches. /// @@ -28,7 +28,7 @@ use crate::vec::Vec; /// /// # Examples /// -/// Let's write the numbers one through ten to a [`TcpStream`]: +/// Let's write the numbers one through ten to a `TcpStream`: /// /// ```no_run /// use std::io::prelude::*; @@ -62,8 +62,6 @@ use crate::vec::Vec; /// 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 /// [`flush`]: BufWriter::flush #[stable(feature = "rust1", since = "1.0.0")] pub struct BufWriter { diff --git a/library/alloc/src/io/buffered/linewritershim.rs b/library/alloc/src/io/buffered/linewritershim.rs index d1e6b99f370d1..8243431816033 100644 --- a/library/alloc/src/io/buffered/linewritershim.rs +++ b/library/alloc/src/io/buffered/linewritershim.rs @@ -12,13 +12,14 @@ use crate::io::{self, BufWriter, IoSlice, Write}; /// implementation details of BufWriter. This also allows existing /// `BufWriters` to be temporarily given line-buffering logic; this is what /// enables Stdout to be alternately in line-buffered or block-buffered mode. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[derive(Debug)] pub struct LineWriterShim<'a, W: ?Sized + Write> { buffer: &'a mut BufWriter, } impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { - #[expect(missing_docs)] pub fn new(buffer: &'a mut BufWriter) -> Self { Self { buffer } } diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index a8c29c1acef26..906be67e1dd05 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -26,12 +26,14 @@ use crate::vec::Vec; /// [`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 +/// 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. /// +/// [`BufRead`]: crate::io::BufRead +/// /// # Examples /// -/// [`File`]s implement `Read`: +/// `File`s implement `Read`: /// /// ```no_run /// use std::io; @@ -78,8 +80,8 @@ use crate::vec::Vec; /// /// [`read()`]: Read::read /// [`&str`]: prim@str -/// [`std::io`]: self -/// [`File`]: crate::fs::File +/// [`std::io`]: crate::io +/// [`BufReader`]: crate::io::BufReader #[stable(feature = "rust1", since = "1.0.0")] #[doc(notable_trait)] #[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] @@ -99,9 +101,9 @@ pub trait Read { /// 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`], + /// 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, + /// 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. @@ -128,7 +130,7 @@ pub trait Read { /// 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 + /// [`MaybeUninit`]: core::mem::MaybeUninit /// /// # Errors /// @@ -141,11 +143,10 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: + /// `File`s implement `Read`: /// /// [`Ok(n)`]: Ok - /// [`File`]: crate::fs::File - /// [`TcpStream`]: crate::net::TcpStream + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted /// /// ```no_run /// use std::io; @@ -214,11 +215,11 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: + /// `File`s implement `Read`: /// - /// [`read()`]: Read::read /// [`Ok(0)`]: Ok - /// [`File`]: crate::fs::File + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`read()`]: Read::read /// /// ```no_run /// use std::io; @@ -235,11 +236,9 @@ pub trait Read { /// } /// ``` /// - /// (See also the [`std::fs::read`] convenience function for reading from a + /// (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 @@ -283,8 +282,8 @@ pub trait Read { /// /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution /// - ///[`read`]: Read::read - /// + /// [`read`]: Read::read + /// [`BufReader`]: crate::io::BufReader /// [`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 { @@ -307,9 +306,7 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File + /// `File`s implement `Read`: /// /// ```no_run /// use std::io; @@ -325,7 +322,7 @@ pub trait Read { /// } /// ``` /// - /// (See also the [`std::fs::read_to_string`] convenience function for + /// (See also the `std::fs::read_to_string` convenience function for /// reading from a file.) /// /// # Usage Notes @@ -339,9 +336,8 @@ pub trait Read { /// /// 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 + /// [`BufReader`]: crate::io::BufReader + /// [`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) @@ -376,10 +372,11 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: + /// `File`s implement `Read`: /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof /// [`read`]: Read::read - /// [`File`]: crate::fs::File /// /// ```no_run /// use std::io; @@ -431,6 +428,9 @@ pub trait Read { /// 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) @@ -443,9 +443,7 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File + /// `File`s implement `Read`: /// /// ```no_run /// use std::io; @@ -487,16 +485,16 @@ pub trait Read { /// /// 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. + /// such as `File`. Consider using a [`BufReader`] in such cases. /// /// # Examples /// - /// [`File`]s implement `Read`: + /// `File`s implement `Read`: /// /// [`Item`]: Iterator::Item - /// [`File`]: crate::fs::File "fs::File" - /// [Result]: crate::result::Result "Result" - /// [io::Error]: self::Error "io::Error" + /// [Result]: core::result::Result "Result" + /// [io::Error]: crate::io::Error "io::Error" + /// [`BufReader`]: crate::io::BufReader /// /// ```no_run /// use std::io; @@ -529,9 +527,7 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File + /// `File`s implement `Read`: /// /// ```no_run /// use std::io; @@ -568,9 +564,8 @@ pub trait Read { /// /// # Examples /// - /// [`File`]s implement `Read`: + /// `File`s implement `Read`: /// - /// [`File`]: crate::fs::File /// [`Ok(0)`]: Ok /// [`read()`]: Read::read /// @@ -609,6 +604,8 @@ pub trait Read { /// 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; @@ -695,7 +692,8 @@ pub trait Read { /// /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution /// -///[`read`]: Read::read +/// [`BufReader`]: crate::io::BufReader +/// [`read`]: Read::read /// #[stable(feature = "io_read_to_string", since = "1.65.0")] pub fn read_to_string(mut reader: R) -> Result { diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs index eda592de6b812..94865a1d11ecc 100644 --- a/library/core/src/io/seek.rs +++ b/library/core/src/io/seek.rs @@ -8,9 +8,7 @@ use crate::io::Result; /// /// # Examples /// -/// [`File`]s implement `Seek`: -/// -/// [`File`]: ../../std/fs/struct.File.html +/// `File`s implement `Seek`: /// /// ```no_run /// use std::io; @@ -152,7 +150,7 @@ pub trait Seek { /// /// 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. + /// such as `BufReader` to perform more efficient seeks. /// /// # Example /// @@ -169,8 +167,6 @@ pub trait Seek { /// Ok(()) /// } /// ``` - /// - /// [`BufReader`]: ../../std/io/struct.BufReader.html #[stable(feature = "seek_seek_relative", since = "1.80.0")] fn seek_relative(&mut self, offset: i64) -> Result<()> { self.seek(SeekFrom::Current(offset))?; diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index 282b3322ef02a..91d3d2c2f6787 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -20,7 +20,7 @@ use crate::io::{Error, IoSlice, Result}; /// /// [`write`]: Write::write /// [`flush`]: Write::flush -/// [`std::io`]: self +/// [`std::io`]: crate::io /// /// # Examples /// @@ -79,6 +79,8 @@ pub trait Write { /// 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 @@ -184,6 +186,8 @@ pub trait Write { /// 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 @@ -230,6 +234,8 @@ pub trait Write { /// 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 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]