From abc324c79f9d9f2fb9eff05e3c7257f7020b7857 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 15:40:10 +1000 Subject: [PATCH 01/14] Move `std::io::default_write_vectored` to `core::io` --- library/alloc/src/io/mod.rs | 4 +++- library/core/src/io/mod.rs | 2 ++ library/core/src/io/write.rs | 11 +++++++++++ library/std/src/io/mod.rs | 10 +--------- 4 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 library/core/src/io/write.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 44a1d1b71d164..91bf95ab589be 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -18,4 +18,6 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; +pub use core::io::{ + IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, stream_len_default, take, +}; diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 1c27e42229bb7..36ec7d8d2adff 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,6 +8,7 @@ mod io_slice; mod seek; mod size_hint; mod util; +mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; @@ -32,6 +33,7 @@ pub use self::{ seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, + write::default_write_vectored, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs new file mode 100644 index 0000000000000..cd0d2eb37249b --- /dev/null +++ b/library/core/src/io/write.rs @@ -0,0 +1,11 @@ +use crate::io::{IoSlice, Result}; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result +where + F: FnOnce(&[u8]) -> Result, +{ + let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); + write(buf) +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 38c8120d66fc6..f2f3aa8bf8dd9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -312,7 +312,7 @@ pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; pub use alloc_crate::io::{ Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, }; -pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; +pub(crate) use alloc_crate::io::{IoHandle, default_write_vectored, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -549,14 +549,6 @@ where read(buf) } -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { From 55585ea8b44d6c676b24a5ada113f25a312242e7 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 13:58:21 +1000 Subject: [PATCH 02/14] Move `std::io::cursor`internals to `core::io` --- library/alloc/src/io/mod.rs | 3 +- library/core/src/io/cursor.rs | 59 ++++++++++++++++++++++++++++++++++- library/core/src/io/mod.rs | 1 + library/std/src/io/cursor.rs | 52 +++--------------------------- 4 files changed, 65 insertions(+), 50 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 91bf95ab589be..6b05d38125654 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -19,5 +19,6 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, + slice_write_all_vectored, slice_write_vectored, stream_len_default, take, }; diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index c104b426f48df..7ee77a0419fda 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}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -294,6 +295,62 @@ where } } +// Non-resizing write implementation +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let dst = &mut slice[(pos as usize)..]; + let amt = cmp::min(buf.len(), dst.len()); + dst[..amt].copy_from_slice(&buf[..amt]); + *pos_mut += amt as u64; + Ok(amt) +} + +#[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 io::Seek for Cursor where diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 36ec7d8d2adff..92bd55a01538e 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -29,6 +29,7 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + cursor::{slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored}, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5399789b4242f..87b721cf1d4b1 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,8 +4,11 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; +use alloc_crate::io::{ + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, +}; + use crate::alloc::Allocator; -use crate::cmp; use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; @@ -150,53 +153,6 @@ impl Write for Cursor { } } -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - 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, From 66f79f18cd581438c1ed0d80c443baf64c10a14e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 3 Jul 2026 11:09:32 +1000 Subject: [PATCH 03/14] Move `WriteThroughCursor` internals to `alloc::io` --- library/alloc/src/io/cursor.rs | 144 +++++++++++++++++++++++++++++++++ library/alloc/src/io/mod.rs | 5 ++ library/std/src/io/cursor.rs | 131 +----------------------------- 3 files changed, 152 insertions(+), 128 deletions(-) create mode 100644 library/alloc/src/io/cursor.rs diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs new file mode 100644 index 0000000000000..a75d6e7df76ae --- /dev/null +++ b/library/alloc/src/io/cursor.rs @@ -0,0 +1,144 @@ +use crate::alloc::Allocator; +use crate::io::{self, ErrorKind, IoSlice}; +use crate::vec::Vec; + +/// Reserves the required space, and pads the vec with 0s if necessary. +fn reserve_and_pad( + pos_mut: &mut u64, + vec: &mut Vec, + buf_len: usize, +) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + io::const_error!( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + + // For safety reasons, we don't want these numbers to overflow + // otherwise our allocation won't be enough + let desired_cap = pos.saturating_add(buf_len); + if desired_cap > vec.capacity() { + // We want our vec's total capacity + // to have room for (pos+buf_len) bytes. Reserve allocates + // based on additional elements from the length, so we need to + // reserve the difference + cfg_select! { + no_global_oom_handling => { + vec.try_reserve(desired_cap - vec.len())?; + } + _ => { + vec.reserve(desired_cap - vec.len()); + } + } + } + // Pad if pos is above the current len. + if pos > vec.len() { + let diff = pos - vec.len(); + // Unfortunately, `resize()` would suffice but the optimiser does not + // realise the `reserve` it does can be eliminated. So we do it manually + // to eliminate that extra branch + let spare = vec.spare_capacity_mut(); + debug_assert!(spare.len() >= diff); + // Safety: we have allocated enough capacity for this. + // And we are only writing, not reading + unsafe { + spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); + vec.set_len(pos); + } + } + + Ok(pos) +} + +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize +where + A: Allocator, +{ + debug_assert!(vec.capacity() >= pos + buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; + pos + buf.len() +} + +/// Resizing `write_all` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the `Write` +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that `Write` will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Cursor`]: crate::io::Cursor +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub 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 +/// +/// [`Cursor`]: crate::io::Cursor +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub 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) +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 6b05d38125654..0830ac8d5a72e 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; @@ -22,3 +23,7 @@ pub use core::io::{ IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, stream_len_default, take, }; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::cursor::{vec_write_all, vec_write_all_vectored}; diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 87b721cf1d4b1..69b1a3311fe21 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -5,12 +5,13 @@ mod tests; pub use core::io::Cursor; use alloc_crate::io::{ - slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, vec_write_all, + vec_write_all_vectored, }; use crate::alloc::Allocator; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -153,132 +154,6 @@ impl Write for Cursor { } } -/// 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] From ec0534fc4ce9686d01c3ab916d3653bc95ec7b39 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 3 Jul 2026 11:37:13 +1000 Subject: [PATCH 04/14] Move `std::io::Write` to `core::io` --- library/alloc/src/io/cursor.rs | 142 +++++++- library/alloc/src/io/impls.rs | 199 ++++++++++- library/alloc/src/io/mod.rs | 11 +- library/alloc/src/lib.rs | 2 + library/core/src/io/cursor.rs | 127 ++++++- library/core/src/io/impls.rs | 147 +++++++- library/core/src/io/mod.rs | 8 +- library/core/src/io/util.rs | 158 ++++++++- library/core/src/io/write.rs | 408 +++++++++++++++++++++- library/std/src/io/cursor.rs | 246 +------------- library/std/src/io/impls.rs | 311 +---------------- library/std/src/io/mod.rs | 409 +---------------------- library/std/src/io/util.rs | 161 +-------- library/std/src/lib.rs | 2 + tests/ui/suggestions/issue-105645.stderr | 2 +- 15 files changed, 1188 insertions(+), 1145 deletions(-) diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index a75d6e7df76ae..21f6b8b5b3ffd 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -1,5 +1,9 @@ use crate::alloc::Allocator; -use crate::io::{self, ErrorKind, IoSlice}; +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. @@ -68,17 +72,15 @@ where /// 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` +/// 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, +/// This means that [`Write`] will pad the vec with 0 initially, /// before writing anything from that point /// -/// [`Cursor`]: crate::io::Cursor -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +/// [`Write`]: crate::io::Write +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result where A: Allocator, { @@ -103,17 +105,15 @@ where /// 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` +/// 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, +/// This means that [`Write`] will pad the vec with 0 initially, /// before writing anything from that point /// -/// [`Cursor`]: crate::io::Cursor -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn vec_write_all_vectored( +/// [`Write`]: crate::io::Write +fn vec_write_all_vectored( pos_mut: &mut u64, vec: &mut Vec, bufs: &[IoSlice<'_>], @@ -142,3 +142,119 @@ where *pos_mut += buf_len as u64; Ok(buf_len) } + +#[stable(feature = "cursor_mut_vec", since = "1.25.0")] +impl WriteThroughCursor for &mut Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl WriteThroughCursor for Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_box_slice", since = "1.5.0")] +impl WriteThroughCursor for Box<[u8], A> +where + A: Allocator, +{ + #[inline] + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + #[inline] + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 732842bc65df7..0f6bfe244dba2 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,7 +1,12 @@ +use crate::alloc::Allocator; use crate::boxed::Box; -use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::fmt; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; +use crate::vec::Vec; // ============================================================================= // Forwarding implementations @@ -20,6 +25,43 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for Box { #[inline] @@ -51,6 +93,161 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Write is implemented for `Vec` by appending to the vector. +/// The vector will grow as needed. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + ::write_all(self, buf)?; + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + cfg_select! { + no_global_oom_handling => { + self.try_reserve(len)?; + } + _ => { + self.reserve(len); + } + } + for buf in bufs { + ::write_all(self, buf)?; + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + cfg_select! { + no_global_oom_handling => { + let n = buf.len(); + self.try_reserve(n)?; + // SAFETY: + // * self and buf are non-overlapping + // * self[..len] is already initialized + // * self[len..len + n] is initialized by copy_nonoverlapping + // * len + n is within the capacity of self based on the reservation completed above + unsafe { + let len = self.len(); + let src = buf.as_ptr(); + let dst = self.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, n); + self.set_len(len + n); + } + } + _ => { + self.extend_from_slice(buf); + } + } + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + self.reserve(len); + for buf in bufs { + self.extend(&**buf); + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend(buf); + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc +where + for<'a> &'a W: Write, + W: crate::io::IoHandle, +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (&**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (&**self).write_fmt(fmt) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Seek for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 0830ac8d5a72e..678934b91da71 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -15,15 +15,12 @@ pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, - Sink, Take, empty, repeat, sink, + Sink, Take, Write, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, chain, default_write_vectored, slice_write, slice_write_all, - slice_write_all_vectored, slice_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_fmt, + default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, stream_len_default, take, }; - -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::cursor::{vec_write_all, vec_write_all_vectored}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 01b4e6f938616..9f82f5be82a68 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -96,6 +96,7 @@ #![feature(async_iterator)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(case_ignorable)] #![feature(cast_maybe_uninit)] #![feature(cell_get_cloned)] @@ -180,6 +181,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 7ee77a0419fda..15b313142c963 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,5 +1,5 @@ use crate::cmp; -use crate::io::{self, ErrorKind, IoSlice, SeekFrom}; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -351,6 +351,81 @@ pub fn slice_write_all_vectored( } Ok(()) } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_array", since = "1.61.0")] +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where @@ -385,3 +460,53 @@ where Ok(self.position()) } } + +/// Trait used to allow indirect implementation of `Write` for `Cursor`. +/// Since [`Cursor`] is not a foundational type, it is not possible to implement +/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a +/// downstream crate (e.g., `liballoc` or `libstd`). +/// +/// Methods are identical in purpose and meaning to their `Write` namesakes. +#[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..218bd7adc2a55 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, u8> { + #[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 92bd55a01538e..98680ad390afc 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -25,16 +25,20 @@ pub use self::{ io_slice::{IoSlice, IoSliceMut}, seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, + write::Write, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - cursor::{slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored}, + cursor::{ + WriteThroughCursor, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, + }, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, - write::default_write_vectored, + write::{default_write_fmt, default_write_vectored}, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index de37690436c80..4ffd38a906f1c 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,4 @@ -use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; +use crate::io::{self, ErrorKind, IoSlice, Result, Seek, SeekFrom, SizeHint, Write}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty @@ -23,6 +23,84 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "empty_seek", since = "1.51.0")] impl Seek for Empty { #[inline] @@ -142,6 +220,84 @@ impl fmt::Debug for Repeat { #[derive(Copy, Clone, Debug, Default)] pub struct Sink; +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// Creates an instance of a writer which will successfully consume all data. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index cd0d2eb37249b..91d3d2c2f6787 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -1,4 +1,370 @@ -use crate::io::{IoSlice, Result}; +use crate::fmt; +use crate::io::{Error, IoSlice, Result}; + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adapters and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: Write::write +/// [`flush`]: Write::flush +/// [`std::io`]: crate::io +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; +/// let mut buffer = File::create("foo.txt")?; +/// +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } +/// Ok(()) +/// } +/// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: Write::write_all +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] +pub trait Write { + /// Writes a buffer into this writer, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write might not succeed, or the write may also generate an + /// error. Typically, a call to `write` represents one attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + /// + /// [`Ok(n)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, buf: &[u8]) -> Result; + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer + /// read from possibly being only partially consumed. This method must + /// behave as a call to [`write`] with the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty + /// buffer provided, or an empty one if none exists. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::IoSlice; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let data1 = [1; 8]; + /// let data2 = [15; 8]; + /// let io_slice1 = IoSlice::new(&data1); + /// let io_slice2 = IoSlice::new(&data2); + /// + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write_vectored(&[io_slice1, io_slice2])?; + /// Ok(()) + /// } + /// ``` + /// + /// [`write`]: Write::write + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + default_write_vectored(|b| self.write(b), bufs) + } + + /// Determines if this `Write`r has an efficient [`write_vectored`] + /// implementation. + /// + /// If a `Write`r does not override the default [`write_vectored`] + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + /// + /// [`write_vectored`]: Write::write_vectored + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + + /// Flushes this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write_all(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write`]. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Unlike [`write_vectored`], this takes a *mutable* reference to + /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to [`write_vectored`] were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and + /// can be reused. + /// + /// [`write_vectored`]: Write::write_vectored + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + // Guarantee that bufs is empty if it contains no data, + // to avoid calling write_vectored if there is no data to be written. + IoSlice::advance_slices(&mut bufs, 0); + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => IoSlice::advance_slices(&mut bufs, n), + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!()`] macro, and it is rare that this should + /// explicitly be called. The [`write!()`] macro should be favored to + /// invoke this method instead. + /// + /// This function internally uses the [`write_all`] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [`write_all`]: Write::write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + if let Some(s) = args.as_statically_known_str() { + self.write_all(s.as_bytes()) + } else { + default_write_fmt(self, args) + } + } + + /// Creates a "by reference" adapter for this instance of `Write`. + /// + /// The returned adapter also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] @@ -9,3 +375,43 @@ where let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); write(buf) } + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_fmt(this: &mut W, args: fmt::Arguments<'_>) -> Result<()> { + // Create a shim which translates a `Write` to a `fmt::Write` and saves off + // I/O errors, instead of discarding them. + struct Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { inner: this, error: Ok(()) }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying stream did not" + ); + } + } + } +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 69b1a3311fe21..e0b127bef8e2b 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,14 +4,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use alloc_crate::io::{ - slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, vec_write_all, - vec_write_all_vectored, -}; - -use crate::alloc::Allocator; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -105,241 +99,3 @@ where self.set_position(self.position() + amt as u64); } } - -/// Trait used to allow indirect implementation of `Write` for `Cursor`. -/// Since [`Cursor`] is not a foundational type, it is not possible to implement -/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a -/// downstream crate (e.g., `liballoc` or `libstd`). -/// -/// Methods are identical in purpose and meaning to their `Write` namesakes. -trait WriteThroughCursor: Sized { - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; - fn is_write_vectored(this: &Cursor) -> bool; - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; - fn flush(this: &mut Cursor) -> io::Result<()>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - WriteThroughCursor::write(self, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - WriteThroughCursor::write_vectored(self, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - WriteThroughCursor::is_write_vectored(self) - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - WriteThroughCursor::write_all(self, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - WriteThroughCursor::write_all_vectored(self, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - WriteThroughCursor::flush(self) - } -} - -#[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 08bb34376b075..61f82828149aa 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, u8> { - #[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 f2f3aa8bf8dd9..b1dbcf1f5bd46 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,13 +306,16 @@ 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; +#[allow(unused_imports, reason = "only used by certain platforms")] +pub(crate) use alloc_crate::io::default_write_vectored; #[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, Seek, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, + repeat, sink, }; -pub(crate) use alloc_crate::io::{IoHandle, default_write_vectored, stream_len_default}; +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}; @@ -338,7 +341,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, fmt, slice, str}; +use crate::{cmp, slice, str}; mod buffered; pub(crate) mod copy; @@ -592,47 +595,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'. @@ -1395,365 +1357,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 ce2137f9567c7..ac3e0f84d1e80 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 db37df63c5763..7cf576ecd8803 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -322,6 +322,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -389,6 +390,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(write_all_vectored)] // tidy-alphabetical-end // // Library features (alloc): diff --git a/tests/ui/suggestions/issue-105645.stderr b/tests/ui/suggestions/issue-105645.stderr index 2b9a94d8e0ab2..2342c22cfa80a 100644 --- a/tests/ui/suggestions/issue-105645.stderr +++ b/tests/ui/suggestions/issue-105645.stderr @@ -7,7 +7,7 @@ LL | foo(&mut bref); | required by a bound introduced by this call | help: the trait `std::io::Write` is implemented for `&mut [u8]` - --> $SRC_DIR/std/src/io/impls.rs:LL:COL + --> $SRC_DIR/core/src/io/impls.rs:LL:COL note: required by a bound in `foo` --> $DIR/issue-105645.rs:8:21 | From 4ecb0cd6a621e90ecd6bd2b0054091b7be853de8 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:28:42 +1000 Subject: [PATCH 05/14] Fix documentation links to `Write` --- library/core/src/io/cursor.rs | 2 +- library/core/src/io/error.rs | 4 ++-- library/core/src/io/mod.rs | 2 +- library/core/src/io/util.rs | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 15b313142c963..645e69a4278e5 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -23,7 +23,7 @@ use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 4214a5f87e3ef..eac7396bcfd83 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -79,7 +79,7 @@ pub type Result = result::Result; /// // FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html +/// [Write]: crate::io::Write /// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] @@ -853,7 +853,7 @@ pub enum ErrorKind { /// particular number of bytes but only a smaller number of bytes could be /// written. /// - /// [write]: ../../std/io/trait.Write.html#tymethod.write + /// [write]: crate::io::Write::write /// [`Ok(0)`]: Ok #[stable(feature = "rust1", since = "1.0.0")] WriteZero, diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 98680ad390afc..0879d14d7ee04 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -55,7 +55,7 @@ pub use self::{ // FIXME(#74481): Hard-links required to link from `core` to `std` /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index 4ffd38a906f1c..b022d876eb8f5 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -4,7 +4,7 @@ use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. /// -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Read`]: ../../std/io/trait.Read.html /// /// This struct is generally created by calling [`empty()`]. Please @@ -129,7 +129,7 @@ impl Seek for Empty { /// [`Ok(buf.len())`]: Ok /// [`Ok(0)`]: Ok /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`read`]: ../../std/io/trait.Read.html#tymethod.read /// /// # Examples @@ -303,7 +303,7 @@ impl Write for &Sink { /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`Ok(buf.len())`]: Ok /// /// # Examples From 53727e6e468fb9a0fd8ab30f1f176605e8f5cc4d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 7 Jul 2026 11:23:55 +1000 Subject: [PATCH 06/14] Add documentation to `core::io` internal methods Co-Authored-By: Clar Fon <15850505+clarfonthey@users.noreply.github.com> --- library/core/src/io/cursor.rs | 9 ++++++++- library/core/src/io/write.rs | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 645e69a4278e5..fe4def531a84a 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -295,7 +295,8 @@ where } } -// Non-resizing write implementation +/// Non-resizing [`Write::write`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. #[inline] #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] @@ -308,6 +309,8 @@ pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Resul Ok(amt) } +/// Non-resizing [`Write::write_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. #[inline] #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] @@ -327,6 +330,8 @@ pub fn slice_write_vectored( Ok(nwritten) } +/// Non-resizing [`Write::write_all`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. #[inline] #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] @@ -335,6 +340,8 @@ pub fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::R if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } } +/// Non-resizing [`Write::write_all_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. #[inline] #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index 91d3d2c2f6787..90cfb94582d7f 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -366,6 +366,8 @@ pub trait Write { } } +/// Default implementation of [`Write::write_vectored`], which is currently used +/// in `libstd` for file system implementations of similar methods. #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result From 0e6181228a88e7e0f37591aca3cb64a8b0fdecf9 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 7 Jul 2026 12:12:19 +1000 Subject: [PATCH 07/14] Reduce visibility of `core::io::write::default_write_fmt` Co-Authored-By: Clar Fon <15850505+clarfonthey@users.noreply.github.com> --- library/alloc/src/io/mod.rs | 6 +++--- library/core/src/io/mod.rs | 2 +- library/core/src/io/write.rs | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 678934b91da71..ef7e95ed5bd7c 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -20,7 +20,7 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use core::io::{ - IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_fmt, - default_write_vectored, slice_write, slice_write_all, slice_write_all_vectored, - slice_write_vectored, stream_len_default, take, + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_vectored, + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, + stream_len_default, take, }; diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 0879d14d7ee04..0134540ae86c8 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -38,7 +38,7 @@ pub use self::{ seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, - write::{default_write_fmt, default_write_vectored}, + write::default_write_vectored, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index 90cfb94582d7f..cdddd380885f4 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -378,9 +378,7 @@ where 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<()> { +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> { From cb09bff72240065593630cac6c54aa15e691f26f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 7 Jul 2026 13:52:32 +1000 Subject: [PATCH 08/14] Add `Vec::try_extend_from_slice_of_bytes` Moves unsafe code from `alloc::io` into `alloc::vec`. A more general `try_extend_from_slice` may be useful in the future, but that requires changing how specialization is done for `SpecExtend` and that's a lot for an internal-only function. --- library/alloc/src/io/impls.rs | 15 +-------------- library/alloc/src/vec/mod.rs | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 0f6bfe244dba2..1351d3e3ef3a1 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -129,20 +129,7 @@ impl Write for Vec { fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { cfg_select! { no_global_oom_handling => { - let n = buf.len(); - self.try_reserve(n)?; - // SAFETY: - // * self and buf are non-overlapping - // * self[..len] is already initialized - // * self[len..len + n] is initialized by copy_nonoverlapping - // * len + n is within the capacity of self based on the reservation completed above - unsafe { - let len = self.len(); - let src = buf.as_ptr(); - let dst = self.as_mut_ptr().add(len); - core::ptr::copy_nonoverlapping(src, dst, n); - self.set_len(len + n); - } + self.try_extend_from_slice_of_bytes(buf)?; } _ => { self.extend_from_slice(buf); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c3b86a635bd0f..a5d03ef76ade6 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2938,8 +2938,26 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { + self.reserve(other.len()); + unsafe { + self.append_elements_unreserved(other); + } + } + + /// Appends elements to `self` from other buffer, returning [`TryReserveError`] on OOM. + #[inline] + unsafe fn try_append_elements(&mut self, other: *const [T]) -> Result<(), TryReserveError> { + self.try_reserve(other.len())?; + unsafe { + self.append_elements_unreserved(other); + } + Ok(()) + } + + /// Appends elements to `self` from other buffer without reserving additional capacity. + #[inline] + unsafe fn append_elements_unreserved(&mut self, other: *const [T]) { let count = other.len(); - self.reserve(count); let len = self.len(); if count > 0 { unsafe { @@ -3609,6 +3627,22 @@ impl Vec { } } +impl Vec { + #[cfg_attr( + not(no_global_oom_handling), + expect( + dead_code, + reason = "currently only used in IO module when global OOM handling is disabled" + ) + )] + pub(crate) fn try_extend_from_slice_of_bytes( + &mut self, + other: &[u8], + ) -> Result<(), TryReserveError> { + unsafe { self.try_append_elements(other) } + } +} + impl Vec<[T; N], A> { /// Takes a `Vec<[T; N]>` and flattens it into a `Vec`. /// From eac111fa8a71f4e41eb1bd21630e9e2f6b39159b Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:25:22 +1000 Subject: [PATCH 09/14] Move `std::io::Read` internals to `alloc::io` --- library/alloc/src/io/mod.rs | 5 +++++ library/alloc/src/io/read.rs | 31 +++++++++++++++++++++++++++++++ library/alloc/src/lib.rs | 1 + library/std/src/io/mod.rs | 25 ++++--------------------- library/std/src/sys/io/mod.rs | 6 ++---- 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 library/alloc/src/io/read.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index ef7e95ed5bd7c..7be1b18474d65 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -3,6 +3,7 @@ mod cursor; mod error; mod impls; +mod read; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -24,3 +25,7 @@ pub use core::io::{ slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, stream_len_default, take, }; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::read::{DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs new file mode 100644 index 0000000000000..e5e459e7d891d --- /dev/null +++ b/library/alloc/src/io/read.rs @@ -0,0 +1,31 @@ +use crate::io::{BorrowedCursor, IoSliceMut, Result}; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const DEFAULT_BUF_SIZE: usize = cfg_select! { + target_os = "espidf" => { 512 }, + _ => { 8 * 1024 } +}; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result +where + F: FnOnce(&mut [u8]) -> Result, +{ + let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); + read(buf) +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_, u8>) -> Result<()> +where + F: FnOnce(&mut [u8]) -> Result, +{ + let n = read(cursor.ensure_init())?; + cursor.advance_checked(n); + Ok(()) +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 9f82f5be82a68..8f7638e5fd43a 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -94,6 +94,7 @@ #![feature(ascii_char)] #![feature(async_fn_traits)] #![feature(async_iterator)] +#![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(can_vector)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b1dbcf1f5bd46..da66fca33fa9d 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,8 +306,6 @@ 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; -#[allow(unused_imports, reason = "only used by certain platforms")] -pub(crate) use alloc_crate::io::default_write_vectored; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] @@ -315,6 +313,10 @@ pub use alloc_crate::io::{ Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, repeat, sink, }; +#[allow(unused_imports, reason = "only used by certain platforms")] +pub(crate) use alloc_crate::io::{ + DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored, default_write_vectored, +}; pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -353,8 +355,6 @@ pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - pub(crate) use stdio::cleanup; struct Guard<'a> { @@ -544,14 +544,6 @@ pub(crate) fn default_read_to_string( unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } } -pub(crate) fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result -where - F: FnOnce(&mut [u8]) -> Result, -{ - let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); - read(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { @@ -566,15 +558,6 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } } -pub(crate) fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_, u8>) -> 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<'_, u8>, diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 0158137174087..02a180f4bc295 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -29,6 +29,8 @@ mod is_terminal { mod kernel_copy; +#[allow(unused_imports, reason = "only used by certain target configurations")] +pub use alloc_crate::io::DEFAULT_BUF_SIZE; #[cfg_attr(not(target_os = "linux"), allow(unused_imports))] #[cfg(all( target_family = "unix", @@ -44,7 +46,3 @@ pub use error::set_errno; pub use error::{decode_error_kind, errno, error_string, is_interrupted}; pub use is_terminal::is_terminal; pub use kernel_copy::{CopyState, kernel_copy}; - -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; From ce08acd265b890aceecfbb029b5534e5be0d37b8 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:44:29 +1000 Subject: [PATCH 10/14] Move `std::io::append_to_string` to `alloc::io` --- library/alloc/src/io/mod.rs | 2 +- library/alloc/src/io/read.rs | 55 +++++++++++++++++++++++++++++++++++- library/std/src/io/mod.rs | 53 ++-------------------------------- 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 7be1b18474d65..f1a86db0f3baf 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -28,4 +28,4 @@ pub use core::io::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::read::{DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored}; +pub use self::read::{DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_vectored}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index e5e459e7d891d..96e759d596991 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1,4 +1,6 @@ -use crate::io::{BorrowedCursor, IoSliceMut, Result}; +use crate::io::{BorrowedCursor, Error, IoSliceMut, Result}; +use crate::string::String; +use crate::vec::Vec; // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) @@ -9,6 +11,57 @@ pub const DEFAULT_BUF_SIZE: usize = cfg_select! { _ => { 8 * 1024 } }; +struct Guard<'a> { + buf: &'a mut Vec, + len: usize, +} + +impl Drop for Guard<'_> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } +} + +// Several `read_to_string` and `read_line` methods in the standard library will +// append data into a `String` buffer, but we need to be pretty careful when +// doing this. The implementation will just call `.as_mut_vec()` and then +// delegate to a byte-oriented reading method, but we must ensure that when +// returning we never leave `buf` in a state such that it contains invalid UTF-8 +// in its bounds. +// +// To this end, we use an RAII guard (to protect against panics) which updates +// the length of the string when it is dropped. This guard initially truncates +// the string to the prior length and only after we've validated that the +// new contents are valid UTF-8 do we allow it to set a longer length. +// +// The unsafety in this function is twofold: +// +// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 +// checks. +// 2. We're passing a raw buffer to the function `f`, and it is expected that +// the function only *appends* bytes to the buffer. We'll get undefined +// behavior if existing bytes are overwritten to have non-UTF-8 data. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn append_to_string(buf: &mut String, f: F) -> Result +where + F: FnOnce(&mut Vec) -> Result, +{ + let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; + let ret = f(g.buf); + + // SAFETY: the caller promises to only append data to `buf` + let appended = unsafe { g.buf.get_unchecked(g.len..) }; + if str::from_utf8(appended).is_err() { + ret.and_then(|_| Err(Error::INVALID_UTF8)) + } else { + g.len = g.buf.len(); + ret + } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index da66fca33fa9d..4e8bb67b36329 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -317,7 +317,7 @@ pub use alloc_crate::io::{ pub(crate) use alloc_crate::io::{ DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored, default_write_vectored, }; -pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; +pub(crate) use alloc_crate::io::{IoHandle, append_to_string, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -343,7 +343,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, slice, str}; +use crate::{cmp, slice}; mod buffered; pub(crate) mod copy; @@ -357,55 +357,6 @@ mod util; pub(crate) use stdio::cleanup; -struct Guard<'a> { - buf: &'a mut Vec, - len: usize, -} - -impl Drop for Guard<'_> { - fn drop(&mut self) { - unsafe { - self.buf.set_len(self.len); - } - } -} - -// Several `read_to_string` and `read_line` methods in the standard library will -// append data into a `String` buffer, but we need to be pretty careful when -// doing this. The implementation will just call `.as_mut_vec()` and then -// delegate to a byte-oriented reading method, but we must ensure that when -// returning we never leave `buf` in a state such that it contains invalid UTF-8 -// in its bounds. -// -// To this end, we use an RAII guard (to protect against panics) which updates -// the length of the string when it is dropped. This guard initially truncates -// the string to the prior length and only after we've validated that the -// new contents are valid UTF-8 do we allow it to set a longer length. -// -// The unsafety in this function is twofold: -// -// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 -// checks. -// 2. We're passing a raw buffer to the function `f`, and it is expected that -// the function only *appends* bytes to the buffer. We'll get undefined -// behavior if existing bytes are overwritten to have non-UTF-8 data. -pub(crate) unsafe fn append_to_string(buf: &mut String, f: F) -> Result -where - F: FnOnce(&mut Vec) -> Result, -{ - let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; - let ret = f(g.buf); - - // SAFETY: the caller promises to only append data to `buf` - let appended = unsafe { g.buf.get_unchecked(g.len..) }; - if str::from_utf8(appended).is_err() { - ret.and_then(|_| Err(Error::INVALID_UTF8)) - } else { - g.len = g.buf.len(); - ret - } -} - // Here we must serve many masters with conflicting goals: // // - avoid allocating unless necessary From 22f5ffcbc53a8781ff18f2923e24a938a19480ec Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 21:51:58 +1000 Subject: [PATCH 11/14] Move `std::io::Read` to `alloc::io` --- library/alloc/src/io/cursor.rs | 101 +- library/alloc/src/io/impls.rs | 358 +++++- library/alloc/src/io/mod.rs | 11 +- library/alloc/src/io/read.rs | 940 +++++++++++++- library/alloc/src/io/util.rs | 314 +++++ library/alloc/src/lib.rs | 4 + library/alloc/src/string.rs | 13 + 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 | 1109 +---------------- library/std/src/io/stdio.rs | 2 + library/std/src/io/util.rs | 99 +- library/std/src/lib.rs | 1 + .../jump-to-def/non-local-method.rs | 4 +- 15 files changed, 1754 insertions(+), 1627 deletions(-) create mode 100644 library/alloc/src/io/util.rs diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index 21f6b8b5b3ffd..92f75e89ca048 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -1,11 +1,108 @@ 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<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); + self.set_position(self.position() + (cursor.written() - prev_written) as u64); + + result + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let content = Cursor::split(self).1; + let len = content.len(); + buf.try_reserve(len)?; + cfg_select! { + no_global_oom_handling => { + buf.try_extend_from_slice_of_bytes(content)?; + } + _ => { + buf.extend_from_slice(content); + } + } + self.set_position(self.position() + len as u64); + + Ok(len) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let content = + crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; + let len = content.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + buf.try_push_str(content)?; + } + _ => { + 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 1351d3e3ef3a1..95e1cc6192101 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,9 +1,12 @@ +use core::cmp; + use crate::alloc::Allocator; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::collections::VecDeque; use crate::fmt; -use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; +use crate::string::String; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; use crate::vec::Vec; @@ -11,6 +14,91 @@ use crate::vec::Vec; // ============================================================================= // Forwarding implementations +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &mut R { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } +} #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SizeHint for Box { @@ -93,6 +181,136 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } + + #[inline] + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { + if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; + return Err(io::Error::READ_EXACT_EOF); + } + let (a, b) = self.split_at(cursor.capacity()); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let len = self.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + buf.try_extend_from_slice_of_bytes(*self)?; + } + _ => { + buf.extend_from_slice(*self); + } + } + + *self = &self[len..]; + Ok(len) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; + let len = self.len(); + buf.try_reserve(len)?; + + cfg_select! { + no_global_oom_handling => { + buf.try_push_str(content)?; + } + _ => { + 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")] @@ -150,6 +368,97 @@ impl Write for Vec { } } +/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Read for VecDeque { + /// Fill `buf` with the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `read` will be needed to read the entire content. + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (ref mut front, _) = self.as_slices(); + let n = Read::read(front, buf)?; + self.drain(..n); + Ok(n) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let (front, back) = self.as_slices(); + + // Use only the front buffer if it is big enough to fill `buf`, else use + // the back buffer too. + match buf.split_at_mut_checked(front.len()) { + None => buf.copy_from_slice(&front[..buf.len()]), + Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { + Some((back, _)) => { + buf_front.copy_from_slice(front); + buf_back.copy_from_slice(back); + } + None => { + self.clear(); + return Err(io::Error::READ_EXACT_EOF); + } + }, + } + + self.drain(..buf.len()); + Ok(()) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { + let len = cursor.capacity(); + let (front, back) = self.as_slices(); + + match front.split_at_checked(cursor.capacity()) { + Some((front, _)) => cursor.append(front), + None => { + cursor.append(front); + match back.split_at_checked(cursor.capacity()) { + Some((back, _)) => cursor.append(back), + None => { + cursor.append(back); + self.clear(); + return Err(io::Error::READ_EXACT_EOF); + } + } + } + } + + self.drain(..len); + Ok(()) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + // The total len is known upfront so we can reserve it in a single call. + let len = self.len(); + buf.try_reserve(len)?; + + let (front, back) = self.as_slices(); + buf.extend_from_slice(front); + buf.extend_from_slice(back); + self.clear(); + Ok(len) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + // SAFETY: We only append to the buffer + unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } + } +} /// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. #[cfg(not(no_global_oom_handling))] #[stable(feature = "vecdeque_read_write", since = "1.63.0")] @@ -193,6 +502,53 @@ impl Write for VecDeque { } } +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Read for Arc +where + for<'a> &'a R: Read, + R: crate::io::IoHandle, +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&**self).read(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { + (&**self).read_buf_exact(cursor) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Write for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index f1a86db0f3baf..748ac33c6d582 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -4,6 +4,7 @@ mod cursor; mod error; mod impls; mod read; +mod util; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -26,6 +27,14 @@ pub use core::io::{ stream_len_default, take, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{read::Read, util::Bytes}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::read::{DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_vectored}; +pub use self::{ + read::{ + DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, + default_read_exact, default_read_to_end, default_read_to_string, default_read_vectored, + }, + util::{SpecReadByte, bytes, uninlined_slow_read_byte}, +}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 96e759d596991..6b07f1c33bda8 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1,7 +1,705 @@ -use crate::io::{BorrowedCursor, Error, IoSliceMut, Result}; +use core::cmp; +use core::mem::MaybeUninit; + +use crate::io::{ + BorrowedBuf, BorrowedCursor, Bytes, Chain, Error, IoSliceMut, Result, Take, bytes, chain, take, +}; use crate::string::String; use crate::vec::Vec; +/// The `Read` trait allows for reading bytes from a source. +/// +/// Implementors of the `Read` trait are called 'readers'. +/// +/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] +/// will attempt to pull bytes from this source into a provided buffer. A +/// number of other methods are implemented in terms of [`read()`], giving +/// implementors a number of ways to read bytes while only needing to implement +/// a single method. +/// +/// Readers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Read` +/// trait. +/// +/// Please note that each call to [`read()`] may involve a system call, and +/// therefore, using something that implements `BufRead`, such as +/// `BufReader`, will be more efficient. +/// +/// Repeated calls to the reader use the same cursor, so for example +/// calling `read_to_end` twice on a `File` will only return the file's +/// contents once. It's recommended to first call `rewind()` in that case. +/// +/// # Examples +/// +/// `File`s implement `Read`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// f.read(&mut buffer)?; +/// +/// let mut buffer = Vec::new(); +/// // read the whole file +/// f.read_to_end(&mut buffer)?; +/// +/// // read into a String, so that you don't need to do the conversion. +/// let mut buffer = String::new(); +/// f.read_to_string(&mut buffer)?; +/// +/// // and more! See the other methods for more details. +/// Ok(()) +/// } +/// ``` +/// +/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: +/// +/// ```no_run +/// # use std::io; +/// use std::io::prelude::*; +/// +/// fn main() -> io::Result<()> { +/// let mut b = "This string will be read".as_bytes(); +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// b.read(&mut buffer)?; +/// +/// // etc... it works exactly as a File does! +/// Ok(()) +/// } +/// ``` +/// +/// [`read()`]: Read::read +/// [`&str`]: prim@str +/// [`std::io`]: crate::io +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + /// + /// This function does not provide any guarantees about whether it blocks + /// waiting for data, but if an object needs to block for a read and cannot, + /// it will typically signal this via an [`Err`] return value. + /// + /// If the return value of this method is [`Ok(n)`], then implementations must + /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// that the buffer `buf` has been filled in with `n` bytes of data from this + /// source. If `n` is `0`, then it can indicate one of two scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer + /// be able to produce bytes. Note that this does not mean that the + /// reader will *always* no longer be able to produce bytes. As an example, + /// on Linux, this method will call the `recv` syscall for a `TcpStream`, + /// where returning zero indicates the connection was shut down correctly. While + /// for `File`, it is possible to reach the end of file and get zero as result, + /// but if more data is appended to the file, future calls to `read` will return + /// more data. + /// 2. The buffer specified was 0 bytes in length. + /// + /// It is not an error if the returned value `n` is smaller than the buffer size, + /// even when the reader is not at the end of the stream yet. + /// This may happen for example because fewer bytes are actually available right now + /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. + /// + /// As this trait is safe to implement, callers in unsafe code cannot rely on + /// `n <= buf.len()` for safety. + /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. + /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if + /// `n > buf.len()`. + /// + /// *Implementations* of this method can make no assumptions about the contents of `buf` when + /// this function is called. It is recommended that implementations only write data to `buf` + /// instead of reading its contents. + /// + /// Correspondingly, however, *callers* of this method in unsafe code must not assume + /// any guarantees about how the implementation uses `buf`. The trait is safe to implement, + /// so it is possible that the code that's supposed to write to the buffer might also read + /// from it. It is your responsibility to make sure that `buf` is initialized + /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one + /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. + /// + /// [`MaybeUninit`]: core::mem::MaybeUninit + /// + /// # Errors + /// + /// If this function encounters any form of I/O or other error, an error + /// variant will be returned. If an error is returned then it must be + /// guaranteed that no bytes were read. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read + /// operation should be retried if there is nothing else to do. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(n)`]: Ok + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read up to 10 bytes + /// let n = f.read(&mut buffer[..])?; + /// + /// println!("The bytes: {:?}", &buffer[..n]); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Like `read`, except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer + /// written to possibly being only partially filled. This method must + /// behave equivalently to a single call to `read` with concatenated + /// buffers. + /// + /// The default implementation calls `read` with either the first nonempty + /// buffer provided, or an empty one if none exists. + #[stable(feature = "iovec", since = "1.36.0")] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + default_read_vectored(|b| self.read(b), bufs) + } + + /// Determines if this `Read`er has an efficient `read_vectored` + /// implementation. + /// + /// If a `Read`er does not override the default `read_vectored` + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + #[unstable(feature = "can_vector", issue = "69941")] + fn is_read_vectored(&self) -> bool { + false + } + + /// Reads all bytes until EOF in this source, placing them into `buf`. + /// + /// All bytes read from this source will be appended to the specified buffer + /// `buf`. This function will continuously call [`read()`] to append more data to + /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of + /// non-[`ErrorKind::Interrupted`] kind. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If any other read error is encountered then this function immediately + /// returns. Any bytes which have already been read will be appended to + /// `buf`. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(0)`]: Ok + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`read()`]: Read::read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// + /// // read the whole file + /// f.read_to_end(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the `std::fs::read` convenience function for reading from a + /// file.) + /// + /// ## Implementing `read_to_end` + /// + /// When implementing the `io::Read` trait, it is recommended to allocate + /// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed + /// by all implementations, and `read_to_end` may not handle out-of-memory + /// situations gracefully. + /// + /// ```no_run + /// # #![expect(dead_code)] + /// # use std::io::{self, BufRead}; + /// # struct Example { example_datasource: io::Empty } impl Example { + /// # fn get_some_data_for_the_example(&self) -> &'static [u8] { &[] } + /// fn read_to_end(&mut self, dest_vec: &mut Vec) -> io::Result { + /// let initial_vec_len = dest_vec.len(); + /// loop { + /// let src_buf = self.example_datasource.fill_buf()?; + /// if src_buf.is_empty() { + /// break; + /// } + /// dest_vec.try_reserve(src_buf.len())?; + /// dest_vec.extend_from_slice(src_buf); + /// + /// // Any irreversible side effects should happen after `try_reserve` succeeds, + /// // to avoid losing data on allocation error. + /// let read = src_buf.len(); + /// self.example_datasource.consume(read); + /// } + /// Ok(dest_vec.len() - initial_vec_len) + /// } + /// # } + /// ``` + /// + /// # Usage Notes + /// + /// `read_to_end` attempts to read a source until EOF, but many sources are continuous streams + /// that do not send EOF. In these cases, `read_to_end` will block indefinitely. Standard input + /// is one such stream which may be finite if piped, but is typically continuous. For example, + /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. + /// Reading user input or running programs that remain open indefinitely will never terminate + /// the stream with `EOF` (e.g. `yes | my-rust-program`). + /// + /// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution + /// + /// [`read`]: Read::read + /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + default_read_to_end(self, buf, None) + } + + /// Reads all bytes until EOF in this source, appending them to `buf`. + /// + /// If successful, this function returns the number of bytes which were read + /// and appended to `buf`. + /// + /// # Errors + /// + /// If the data in this stream is *not* valid UTF-8 then an error is + /// returned and `buf` is unchanged. + /// + /// See [`read_to_end`] for other error semantics. + /// + /// [`read_to_end`]: Read::read_to_end + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = String::new(); + /// + /// f.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the `std::fs::read_to_string` convenience function for + /// reading from a file.) + /// + /// # Usage Notes + /// + /// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams + /// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input + /// is one such stream which may be finite if piped, but is typically continuous. For example, + /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. + /// Reading user input or running programs that remain open indefinitely will never terminate + /// the stream with `EOF` (e.g. `yes | my-rust-program`). + /// + /// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution + /// + /// [`read`]: Read::read + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + default_read_to_string(self, buf, None) + } + + /// Reads the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the + /// specified buffer `buf`. + /// + /// *Implementations* of this method can make no assumptions about the contents of `buf` when + /// this function is called. It is recommended that implementations only write data to `buf` + /// instead of reading its contents. The documentation on [`read`] has a more detailed + /// explanation of this subject. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + /// [`read`]: Read::read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read exactly 10 bytes + /// f.read_exact(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "read_exact", since = "1.6.0")] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + default_read_exact(self, buf) + } + + /// Pull some bytes from this source into the specified buffer. + /// + /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use + /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`. + /// + /// The default implementation delegates to `read`. + /// + /// This method makes it possible to return both data and an error but it is advised against. + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf(&mut self, buf: BorrowedCursor<'_, u8>) -> Result<()> { + default_read_buf(|b| self.read(b), buf) + } + + /// Reads the exact number of bytes required to fill `cursor`. + /// + /// This is similar to the [`read_exact`](Read::read_exact) method, except + /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use + /// with uninitialized buffers. + /// + /// # Errors + /// + /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] + /// then the error is ignored and the operation will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// If any other read error is encountered then this function immediately + /// returns. + /// + /// If this function returns an error, all bytes read will be appended to `cursor`. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_, u8>) -> Result<()> { + default_read_buf_exact(self, cursor) + } + + /// Creates a "by reference" adapter for this instance of `Read`. + /// + /// The returned adapter also implements `Read` and will simply borrow this + /// current reader. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```no_run + /// use std::io; + /// use std::io::Read; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// let mut other_buffer = Vec::new(); + /// + /// { + /// let reference = f.by_ref(); + /// + /// // read at most 5 bytes + /// reference.take(5).read_to_end(&mut buffer)?; + /// + /// } // drop our &mut reference so we can use f again + /// + /// // original file still usable, read the rest + /// f.read_to_end(&mut other_buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + /// Transforms this `Read` instance to an [`Iterator`] over its bytes. + /// + /// The returned type implements [`Iterator`] where the [`Item`] is + /// [Result]<[u8], [io::Error]>. + /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] + /// otherwise. EOF is mapped to returning [`None`] from this iterator. + /// + /// The default implementation calls `read` for each byte, + /// which can be very inefficient for data that's not in memory, + /// such as `File`. Consider using a `BufReader` in such cases. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Item`]: Iterator::Item + /// [Result]: core::result::Result "Result" + /// [io::Error]: crate::io::Error "io::Error" + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = BufReader::new(File::open("foo.txt")?); + /// + /// for byte in f.bytes() { + /// println!("{}", byte?); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn bytes(self) -> Bytes + where + Self: Sized, + { + bytes(self) + } + + /// Creates an adapter which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f1 = File::open("foo.txt")?; + /// let f2 = File::open("bar.txt")?; + /// + /// let mut handle = f1.chain(f2); + /// let mut buffer = String::new(); + /// + /// // read the value into a String. We could use any Read method here, + /// // this is just one example. + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn chain(self, next: R) -> Chain + where + Self: Sized, + { + chain(self, next) + } + + /// Creates an adapter which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + /// read errors will not count towards the number of bytes read and future + /// calls to [`read()`] may succeed. + /// + /// # Examples + /// + /// `File`s implement `Read`: + /// + /// [`Ok(0)`]: Ok + /// [`read()`]: Read::read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// let mut buffer = [0; 5]; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// + /// handle.read(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + take(self, limit) + } + + /// Read and return a fixed array of bytes from this source. + /// + /// This function uses an array sized based on a const generic size known at compile time. You + /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference + /// determine the number of bytes needed based on how the return value gets used. For instance, + /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of + /// bytes into an integer of the same size. + /// + /// Like `read_exact`, if this function encounters an "end of file" before reading the desired + /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof + /// + /// ``` + /// #![feature(read_array)] + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); + /// let x = u64::from_le_bytes(buf.read_array()?); + /// let y = u32::from_be_bytes(buf.read_array()?); + /// let z = u16::from_be_bytes(buf.read_array()?); + /// assert_eq!(x, 0x807060504030201); + /// assert_eq!(y, 0x9080706); + /// assert_eq!(z, 0x504); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_array", issue = "148848")] + fn read_array(&mut self) -> Result<[u8; N]> + where + Self: Sized, + { + let mut buf = [MaybeUninit::uninit(); N]; + let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); + self.read_buf_exact(borrowed_buf.unfilled())?; + // Guard against incorrect `read_buf_exact` implementations. + assert_eq!(borrowed_buf.len(), N); + Ok(unsafe { MaybeUninit::array_assume_init(buf) }) + } + + /// Read and return a type (e.g. an integer) in little-endian order. + /// + /// You can specify the type with turbofish (`reader.read_le::()`), or let type inference + /// determine the type based on how the return value gets used. + /// + /// 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_le)] + /// 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 = buf.read_le()?; + /// let y: u32 = buf.read_le()?; + /// let z = buf.read_le::()?; + /// assert_eq!(x, 0x807060504030201); + /// assert_eq!(y, 0x6070809); + /// assert_eq!(z, 0x405); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_le", issue = "156984")] + #[inline] + fn read_le(&mut self) -> Result + where + Self: Sized, + { + T::read_le_from(self) + } + + /// Read and return a type (e.g. an integer) in big-endian order. + /// + /// You can specify the type with turbofish (`reader.read_be::()`), or let type inference + /// determine the type based on how the return value gets used. + /// + /// 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_le)] + /// 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 = buf.read_be()?; + /// let y: u32 = buf.read_be()?; + /// let z = buf.read_be::()?; + /// assert_eq!(x, 0x102030405060708); + /// assert_eq!(y, 0x9080706); + /// assert_eq!(z, 0x504); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_le", issue = "156984")] + #[inline] + fn read_be(&mut self) -> Result + where + Self: Sized, + { + T::read_be_from(self) + } +} + // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) #[doc(hidden)] @@ -62,6 +760,166 @@ where } } +// Here we must serve many masters with conflicting goals: +// +// - avoid allocating unless necessary +// - avoid overallocating if we know the exact size (#89165) +// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) +// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads +// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems +// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) +// +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_to_end( + r: &mut R, + buf: &mut Vec, + size_hint: Option, +) -> Result { + let start_len = buf.len(); + let start_cap = buf.capacity(); + // Optionally limit the maximum bytes read on each iteration. + // This adds an arbitrary fiddle factor to allow for more data than we expect. + let mut max_read_size = size_hint + .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) + .unwrap_or(DEFAULT_BUF_SIZE); + + const PROBE_SIZE: usize = 32; + + fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { + let mut probe = [0u8; PROBE_SIZE]; + + loop { + cfg_select! { + no_global_oom_handling => { + // Without global OOM handling we must proactively allocate the buffer + // to avoid failing after already reading data. + buf.try_reserve(PROBE_SIZE)?; + } + _ => {} + } + + match r.read(&mut probe) { + Ok(n) => { + cfg_select! { + no_global_oom_handling => { + // there is no way to recover from allocation failure here + // because the data has already been read. + buf.try_extend_from_slice_of_bytes(&probe[..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<'_, u8> = spare.into(); + + // Note that we don't track already initialized bytes here, but this is fine + // because we explicitly limit the read size + let mut cursor = read_buf.unfilled(); + let result = loop { + match r.read_buf(cursor.reborrow()) { + Err(e) if e.is_interrupted() => continue, + // Do not stop now in case of error: we might have received both data + // and an error + res => break res, + } + }; + + let bytes_read = cursor.written(); + let is_init = read_buf.is_init(); + + // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. + unsafe { + let new_len = bytes_read + buf.len(); + buf.set_len(new_len); + } + + // Now that all data is pushed to the vector, we can fail without data loss + result?; + + if bytes_read == 0 { + return Ok(buf.len() - start_len); + } + + // Use heuristics to determine the max read size if no initial size hint was provided + if size_hint.is_none() { + // The reader is returning short reads but it doesn't call ensure_init(). + // In that case we no longer need to restrict read sizes to avoid + // initialization costs. + // When reading from disk we usually don't get any short reads except at EOF. + // So we wait for at least 2 short reads before uncapping the read buffer; + // this helps with the Windows issue. + if !is_init { + max_read_size = usize::MAX; + } + // we have passed a larger buffer than previously and the + // reader still hasn't returned a short read + else if buf_len >= max_read_size && bytes_read == buf_len { + max_read_size = max_read_size.saturating_mul(2); + } + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_to_string( + r: &mut R, + buf: &mut String, + size_hint: Option, +) -> Result { + // Note that we do *not* call `r.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `default_read_to_end` implementation which + // we know is guaranteed to only read data into the end of the buffer. + unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result @@ -72,6 +930,22 @@ where read(buf) } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match this.read(buf) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_, u8>) -> Result<()> @@ -82,3 +956,67 @@ where cursor.advance_checked(n); Ok(()) } + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_read_buf_exact( + this: &mut R, + mut cursor: BorrowedCursor<'_, u8>, +) -> 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(()) +} + +mod sealed { + /// This trait being unreachable from outside the crate + /// prevents outside implementations of our extension traits. + /// This allows adding more trait methods in the future. + #[unstable(feature = "sealed", issue = "none")] + pub trait Sealed {} +} + +/// Trait for types that can be converted from a fixed-size byte array with a specified endianness +#[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] +// Once we can use associated consts in the types of method parameters, rewrite this to have +// `from_le_bytes` and `from_be_bytes` methods, move it to `core`, and make it public. +pub trait FromEndianBytes: sealed::Sealed + Sized { + #[doc(hidden)] + fn read_le_from(r: &mut impl Read) -> Result; + + #[doc(hidden)] + fn read_be_from(r: &mut impl Read) -> Result; +} + +macro_rules! impl_from_endian_bytes { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl sealed::Sealed for $t {} + + #[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] + impl FromEndianBytes for $t { + #[inline] + fn read_le_from(r: &mut impl Read) -> Result { + Ok(<$t>::from_le_bytes(r.read_array()?)) + } + + #[inline] + fn read_be_from(r: &mut impl Read) -> Result { + Ok(<$t>::from_be_bytes(r.read_array()?)) + } + } + )*}; +} + +impl_from_endian_bytes!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64); diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs new file mode 100644 index 0000000000000..c2b82a1c25a75 --- /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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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 8f7638e5fd43a..710fef76bb108 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -121,6 +121,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)] @@ -140,6 +141,7 @@ #![feature(inplace_iteration)] #![feature(io_const_error)] #![feature(io_const_error_internals)] +#![feature(io_slice_as_bytes)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] @@ -147,6 +149,7 @@ #![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)] @@ -218,6 +221,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/alloc/src/string.rs b/library/alloc/src/string.rs index eb00db4568b63..a7044e2868f99 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1107,6 +1107,19 @@ impl String { self.vec.extend_from_slice(string.as_bytes()) } + /// Appends a given string slice onto the end of this `String`, returning + /// [`TryReserveError`] otherwise. + #[cfg_attr( + not(no_global_oom_handling), + expect( + dead_code, + reason = "currently only used in IO module when global OOM handling is disabled" + ) + )] + pub(crate) fn try_push_str(&mut self, string: &str) -> Result<(), TryReserveError> { + self.vec.try_extend_from_slice_of_bytes(string.as_bytes()) + } + #[cfg(not(no_global_oom_handling))] #[inline] fn push_str_slice(&mut self, slice: &[&str]) { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 075ea44377c78..8d122c8e76e5f 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -318,6 +318,8 @@ impl BufReader { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SpecReadByte for BufReader where Self: Read, diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index e0b127bef8e2b..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<'_, u8>) -> 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<'_, u8>) -> 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 61f82828149aa..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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> io::Result<()> { - (&**self).read_buf_exact(cursor) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4e8bb67b36329..a20503f9d9205 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,14 +310,17 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, - repeat, sink, + Bytes, Chain, Empty, Error, ErrorKind, Read, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, + empty, repeat, sink, }; #[allow(unused_imports, reason = "only used by certain platforms")] pub(crate) use alloc_crate::io::{ DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored, default_write_vectored, }; -pub(crate) use alloc_crate::io::{IoHandle, append_to_string, stream_len_default}; +pub(crate) use alloc_crate::io::{ + IoHandle, SpecReadByte, append_to_string, default_read_buf_exact, default_read_exact, + default_read_to_end, default_read_to_string, stream_len_default, uninlined_slow_read_byte, +}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -342,8 +345,7 @@ pub use self::{ cursor::Cursor, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -use crate::mem::MaybeUninit; -use crate::{cmp, slice}; +use crate::cmp; mod buffered; pub(crate) mod copy; @@ -357,875 +359,6 @@ mod util; pub(crate) use stdio::cleanup; -// Here we must serve many masters with conflicting goals: -// -// - avoid allocating unless necessary -// - avoid overallocating if we know the exact size (#89165) -// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) -// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads -// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems -// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) -// -pub(crate) fn default_read_to_end( - r: &mut R, - buf: &mut Vec, - size_hint: Option, -) -> Result { - let start_len = buf.len(); - let start_cap = buf.capacity(); - // Optionally limit the maximum bytes read on each iteration. - // This adds an arbitrary fiddle factor to allow for more data than we expect. - let mut max_read_size = size_hint - .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) - .unwrap_or(DEFAULT_BUF_SIZE); - - const PROBE_SIZE: usize = 32; - - fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { - let mut probe = [0u8; PROBE_SIZE]; - - loop { - match r.read(&mut probe) { - Ok(n) => { - // there is no way to recover from allocation failure here - // because the data has already been read. - buf.extend_from_slice(&probe[..n]); - return Ok(n); - } - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - } - } - - // avoid inflating empty/small vecs before we have determined that there's anything to read - if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(0); - } - } - - loop { - if buf.len() == buf.capacity() && buf.capacity() == start_cap { - // The buffer might be an exact fit. Let's read into a probe buffer - // and see if it returns `Ok(0)`. If so, we've avoided an - // unnecessary doubling of the capacity. But if not, append the - // probe buffer to the primary buffer and let its capacity grow. - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(buf.len() - start_len); - } - } - - if buf.len() == buf.capacity() { - // buf is full, need more space - buf.try_reserve(PROBE_SIZE)?; - } - - let mut spare = buf.spare_capacity_mut(); - let buf_len = cmp::min(spare.len(), max_read_size); - spare = &mut spare[..buf_len]; - let mut read_buf: BorrowedBuf<'_, u8> = spare.into(); - - // Note that we don't track already initialized bytes here, but this is fine - // because we explicitly limit the read size - let mut cursor = read_buf.unfilled(); - let result = loop { - match r.read_buf(cursor.reborrow()) { - Err(e) if e.is_interrupted() => continue, - // Do not stop now in case of error: we might have received both data - // and an error - res => break res, - } - }; - - let bytes_read = cursor.written(); - let is_init = read_buf.is_init(); - - // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. - unsafe { - let new_len = bytes_read + buf.len(); - buf.set_len(new_len); - } - - // Now that all data is pushed to the vector, we can fail without data loss - result?; - - if bytes_read == 0 { - return Ok(buf.len() - start_len); - } - - // Use heuristics to determine the max read size if no initial size hint was provided - if size_hint.is_none() { - // The reader is returning short reads but it doesn't call ensure_init(). - // In that case we no longer need to restrict read sizes to avoid - // initialization costs. - // When reading from disk we usually don't get any short reads except at EOF. - // So we wait for at least 2 short reads before uncapping the read buffer; - // this helps with the Windows issue. - if !is_init { - max_read_size = usize::MAX; - } - // we have passed a larger buffer than previously and the - // reader still hasn't returned a short read - else if buf_len >= max_read_size && bytes_read == buf_len { - max_read_size = max_read_size.saturating_mul(2); - } - } - } -} - -pub(crate) fn default_read_to_string( - r: &mut R, - buf: &mut String, - size_hint: Option, -) -> Result { - // Note that we do *not* call `r.read_to_end()` here. We are passing - // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` - // method to fill it up. An arbitrary implementation could overwrite the - // entire contents of the vector, not just append to it (which is what - // we are expecting). - // - // To prevent extraneously checking the UTF-8-ness of the entire buffer - // we pass it to our hardcoded `default_read_to_end` implementation which - // we know is guaranteed to only read data into the end of the buffer. - unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } -} - -pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { - while !buf.is_empty() { - match this.read(buf) { - Ok(0) => break, - Ok(n) => { - buf = &mut buf[n..]; - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } -} - -pub(crate) fn default_read_buf_exact( - this: &mut R, - mut cursor: BorrowedCursor<'_, u8>, -) -> 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<'_, u8>) -> 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<'_, u8>) -> 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) }) - } - - /// Read and return a type (e.g. an integer) in little-endian order. - /// - /// You can specify the type with turbofish (`reader.read_le::()`), or let type inference - /// determine the type based on how the return value gets used. - /// - /// 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_le)] - /// 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 = buf.read_le()?; - /// let y: u32 = buf.read_le()?; - /// let z = buf.read_le::()?; - /// assert_eq!(x, 0x807060504030201); - /// assert_eq!(y, 0x6070809); - /// assert_eq!(z, 0x405); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "read_le", issue = "156984")] - #[inline] - fn read_le(&mut self) -> Result - where - Self: Sized, - { - T::read_le_from(self) - } - - /// Read and return a type (e.g. an integer) in big-endian order. - /// - /// You can specify the type with turbofish (`reader.read_be::()`), or let type inference - /// determine the type based on how the return value gets used. - /// - /// 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_le)] - /// 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 = buf.read_be()?; - /// let y: u32 = buf.read_be()?; - /// let z = buf.read_be::()?; - /// assert_eq!(x, 0x102030405060708); - /// assert_eq!(y, 0x9080706); - /// assert_eq!(z, 0x504); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "read_le", issue = "156984")] - #[inline] - fn read_be(&mut self) -> Result - where - Self: Sized, - { - T::read_be_from(self) - } -} - /// Reads all bytes from a [reader][Read] into a new [`String`]. /// /// This is a convenience function for [`Read::read_to_string`]. Using this @@ -1754,65 +887,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<'_, u8>) -> 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]> { @@ -1848,79 +922,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<'_, u8>) -> 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]> { @@ -1942,71 +943,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. /// @@ -2075,34 +1011,3 @@ impl Iterator for Lines { } } } - -/// Trait for types that can be converted from a fixed-size byte array with a specified endianness -#[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] -// Once we can use associated consts in the types of method parameters, rewrite this to have -// `from_le_bytes` and `from_be_bytes` methods, move it to `core`, and make it public. -pub trait FromEndianBytes: crate::sealed::Sealed + Sized { - #[doc(hidden)] - fn read_le_from(r: &mut impl Read) -> Result; - - #[doc(hidden)] - fn read_be_from(r: &mut impl Read) -> Result; -} - -macro_rules! impl_from_endian_bytes { - ($($t:ty),*$(,)?) => {$( - #[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] - impl FromEndianBytes for $t { - #[inline] - fn read_le_from(r: &mut impl Read) -> Result { - Ok(<$t>::from_le_bytes(r.read_array()?)) - } - - #[inline] - fn read_be_from(r: &mut impl Read) -> Result { - Ok(<$t>::from_be_bytes(r.read_array()?)) - } - } - )*}; -} - -impl_from_endian_bytes!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64); diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 713dd296c064a..957235f9f3fb0 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -544,6 +544,8 @@ impl Read for StdinLock<'_> { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SpecReadByte for StdinLock<'_> { #[inline] fn spec_read_byte(&mut self) -> Option> { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index ac3e0f84d1e80..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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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<'_, u8>) -> 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 7cf576ecd8803..e0dfa3b7988e8 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -400,6 +400,7 @@ #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] +#![feature(read_buf)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] diff --git a/tests/rustdoc-html/jump-to-def/non-local-method.rs b/tests/rustdoc-html/jump-to-def/non-local-method.rs index e084899b2495e..e785ab8d204f9 100644 --- a/tests/rustdoc-html/jump-to-def/non-local-method.rs +++ b/tests/rustdoc-html/jump-to-def/non-local-method.rs @@ -6,7 +6,7 @@ //@ has - '//a[@href="{{channel}}/alloc/boxed/struct.Box.html"]' 'std::boxed::Box' use std::boxed::Box; -//@ has - '//a[@href="{{channel}}/std/io/trait.Read.html"]' 'std::io::Read' +//@ has - '//a[@href="{{channel}}/alloc/io/read/trait.Read.html"]' 'std::io::Read' use std::io::Read; //@ has - '//a[@href="{{channel}}/std/io/index.html"]' 'std::io' use std::io; @@ -16,7 +16,7 @@ use std::cmp::Ordering; use std::marker::PhantomData; pub fn bar2(readable: T) { - //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read' + //@ has - '//a[@href="{{channel}}/alloc/io/read/trait.Read.html#tymethod.read"]' 'read' let _ = readable.read(&mut []); } From 4acfc7c90de168479815fba7eb922cefba2b1099 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 13:24:27 +1000 Subject: [PATCH 12/14] 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 748ac33c6d582..a3fc76634d55b 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -27,8 +27,6 @@ pub use core::io::{ stream_len_default, take, }; -#[unstable(feature = "alloc_io", issue = "154046")] -pub use self::{read::Read, util::Bytes}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ @@ -38,3 +36,8 @@ pub use self::{ }, util::{SpecReadByte, bytes, uninlined_slow_read_byte}, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{ + read::{Read, read_to_string}, + util::Bytes, +}; diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 6b07f1c33bda8..b030663818c72 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -700,6 +700,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 a20503f9d9205..d5de33a899e83 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")] @@ -359,71 +361,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 abd3a5997c3023b076127205035d3b3332ecb53a Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:09:23 +1000 Subject: [PATCH 13/14] Move `std::io::BufRead` to `alloc::io` --- library/alloc/src/io/buf_read.rs | 496 +++++++++++++++ library/alloc/src/io/cursor.rs | 18 +- library/alloc/src/io/impls.rs | 101 ++- library/alloc/src/io/mod.rs | 14 +- library/alloc/src/io/util.rs | 172 ++++- library/std/src/io/cursor.rs | 18 - library/std/src/io/impls.rs | 107 ---- library/std/src/io/mod.rs | 596 +----------------- library/std/src/io/util.rs | 35 - library/std/src/lib.rs | 1 + .../where.SWhere_TraitWhere_item-decl.html | 2 +- 11 files changed, 794 insertions(+), 766 deletions(-) create mode 100644 library/alloc/src/io/buf_read.rs diff --git a/library/alloc/src/io/buf_read.rs b/library/alloc/src/io/buf_read.rs new file mode 100644 index 0000000000000..c4467fcb495e3 --- /dev/null +++ b/library/alloc/src/io/buf_read.rs @@ -0,0 +1,496 @@ +use core::slice::memchr; + +use crate::io::{ErrorKind, Lines, Read, Result, Split, append_to_string, lines, split}; +use crate::string::String; +use crate::vec::Vec; + +/// A `BufRead` is a type of [`Read`]er which has an internal buffer, allowing it +/// to perform extra ways of reading. +/// +/// For example, reading line-by-line is inefficient without using a buffer, so +/// if you want to read by line, you'll need `BufRead`, which includes a +/// [`read_line`] method as well as a [`lines`] iterator. +/// +/// # Examples +/// +/// A locked standard input implements `BufRead`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// +/// let stdin = io::stdin(); +/// for line in stdin.lock().lines() { +/// println!("{}", line?); +/// } +/// # std::io::Result::Ok(()) +/// ``` +/// +/// If you have something that implements [`Read`], you can use the `BufReader` +/// type to turn it into a `BufRead`. +/// +/// For example, `File` implements [`Read`], but not `BufRead`. +/// `BufReader` to the rescue! +/// +/// [`read_line`]: BufRead::read_line +/// [`lines`]: BufRead::lines +/// +/// ```no_run +/// use std::io::{self, BufReader}; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let f = File::open("foo.txt")?; +/// let f = BufReader::new(f); +/// +/// for line in f.lines() { +/// let line = line?; +/// println!("{line}"); +/// } +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] +pub trait BufRead: Read { + /// Returns the contents of the internal buffer, filling it with more data, via [`Read`] methods, if empty. + /// + /// This is a lower-level method and is meant to be used together with [`consume`], + /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. + /// + /// [`consume`]: BufRead::consume + /// + /// Returns an empty buffer when the stream has reached EOF. + /// + /// # Errors + /// + /// This function will return an I/O error if a [`Read`] method was called, but returned an error. + /// + /// # Examples + /// + /// A locked standard input implements `BufRead`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// let buffer = stdin.fill_buf()?; + /// + /// // work with buffer + /// println!("{buffer:?}"); + /// + /// // mark the bytes we worked with as read + /// let length = buffer.len(); + /// stdin.consume(length); + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Marks the given `amount` of additional bytes from the internal buffer as having been read. + /// Subsequent calls to `read` only return bytes that have not been marked as read. + /// + /// This is a lower-level method and is meant to be used together with [`fill_buf`], + /// which can be used to fill the internal buffer via [`Read`] methods. + /// + /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. + /// + /// # Examples + /// + /// Since `consume()` is meant to be used with [`fill_buf`], + /// that method's example includes an example of `consume()`. + /// + /// [`fill_buf`]: BufRead::fill_buf + #[stable(feature = "rust1", since = "1.0.0")] + fn consume(&mut self, amount: usize); + + /// Checks if there is any data left to be `read`. + /// + /// This function may fill the buffer to check for data, + /// so this function returns `Result`, not `bool`. + /// + /// The default implementation calls `fill_buf` and checks that the + /// returned slice is empty (which means that there is no data left, + /// since EOF is reached). + /// + /// # Errors + /// + /// This function will return an I/O error if a [`Read`] method was called, but returned an error. + /// + /// Examples + /// + /// ``` + /// #![feature(buf_read_has_data_left)] + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// while stdin.has_data_left()? { + /// let mut line = String::new(); + /// stdin.read_line(&mut line)?; + /// // work with line + /// println!("{line:?}"); + /// } + /// # std::io::Result::Ok(()) + /// ``` + #[unstable(feature = "buf_read_has_data_left", issue = "86423")] + fn has_data_left(&mut self) -> Result { + self.fill_buf().map(|b| !b.is_empty()) + } + + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. + /// + /// This function will read bytes from the underlying stream until the + /// delimiter or EOF is found. Once found, all bytes up to, and including, + /// the delimiter (if found) will be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the bytes in a byte slice + /// in hyphen delimited segments: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); + /// let mut buf = vec![]; + /// + /// // cursor is at 'l' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 6); + /// assert_eq!(buf, b"lorem-"); + /// buf.clear(); + /// + /// // cursor is at 'i' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 5); + /// assert_eq!(buf, b"ipsum"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, b""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + read_until(self, byte, buf) + } + + /// Skips all bytes until the delimiter `byte` or EOF is reached. + /// + /// This function will read (and discard) bytes from the underlying stream until the + /// delimiter or EOF is found. + /// + /// If successful, this function will return the total number of bytes read, + /// including the delimiter byte if found. + /// + /// This is useful for efficiently skipping data such as NUL-terminated strings + /// in binary file formats without buffering. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read some NUL-terminated information + /// about Ferris from a binary string, skipping the fun fact: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); + /// + /// // read name + /// let mut name = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut name) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 7); + /// assert_eq!(name, b"Ferris\0"); + /// + /// // skip fun fact + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 30); + /// + /// // read animal type + /// let mut animal = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut animal) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 11); + /// assert_eq!(animal, b"Crustacean\0"); + /// + /// // reach EOF + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 1); + /// ``` + #[stable(feature = "bufread_skip_until", since = "1.83.0")] + fn skip_until(&mut self, byte: u8) -> Result { + skip_until(self, byte) + } + + /// Reads all bytes until a newline (the `0xA` byte) is reached, and append + /// them to the provided [`String`] buffer. + /// + /// Previous content of the buffer will be preserved. To avoid appending to + /// the buffer, you need to [`clear`] it first. + /// + /// This function will read bytes from the underlying stream until the + /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes + /// up to, and including, the delimiter (if found) will be appended to + /// `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// If this function returns [`Ok(0)`], the stream has reached EOF. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending a newline + /// or EOF. You can use [`take`] to limit the maximum number of bytes read. + /// + /// [`Ok(0)`]: Ok + /// [`clear`]: String::clear + /// [`take`]: crate::io::Read::take + /// + /// # Errors + /// + /// This function has the same error semantics as [`read_until`] and will + /// also return an error if the read bytes are not valid UTF-8. If an I/O + /// error is encountered then `buf` may contain some bytes already read in + /// the event that all data read so far was valid UTF-8. + /// + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the lines in a byte slice: + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"foo\nbar"); + /// let mut buf = String::new(); + /// + /// // cursor is at 'f' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 4); + /// assert_eq!(buf, "foo\n"); + /// buf.clear(); + /// + /// // cursor is at 'b' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 3); + /// assert_eq!(buf, "bar"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, ""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_line(&mut self, buf: &mut String) -> Result { + // Note that we are not calling the `.read_until` method here, but + // rather our hardcoded implementation. For more details as to why, see + // the comments in `default_read_to_string`. + unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } + } + + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + /// + /// The iterator returned from this function will return instances of + /// [io::Result]<[Vec]\>. Each vector returned will *not* have + /// the delimiter byte at the end. + /// + /// This function will yield errors whenever [`read_until`] would have + /// also yielded an error. + /// + /// [io::Result]: self::Result "io::Result" + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all hyphen delimited + /// segments in a byte slice + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + /// + /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); + /// assert_eq!(split_iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + split(self, byte) + } + + /// Returns an iterator over the lines of this reader. + /// + /// The iterator returned from this function will yield instances of + /// [io::Result]<[String]>. Each string returned will *not* have a newline + /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. + /// + /// [io::Result]: crate::io::Result "io::Result" + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all the lines in a byte + /// slice. + /// + /// [`Cursor`]: crate::io::Cursor + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); + /// + /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); + /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); + /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); + /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); + /// assert_eq!(lines_iter.next(), None); + /// ``` + /// + /// # Errors + /// + /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. + #[stable(feature = "rust1", since = "1.0.0")] + fn lines(self) -> Lines + where + Self: Sized, + { + lines(self) + } +} + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + }; + let (done, available) = match memchr::memchr(delim, available) { + Some(i) => (true, &available[..=i]), + None => (false, available), + }; + + cfg_select! { + no_global_oom_handling => { + let count = available.len(); + buf.try_reserve(count)?; + + // SAFETY: + // * self and buf are non-overlapping + // * buf[..len] is already initialized + // * buf[len..len + count] is initialized by copy_nonoverlapping + // * len + count is within the capacity of buf based on the reservation completed above + unsafe { + let len = buf.len(); + let src = available.as_ptr(); + let dst = buf.as_mut_ptr().add(len); + core::ptr::copy_nonoverlapping(src, dst, count); + buf.set_len(len + count); + } + } + _ => { + buf.extend_from_slice(available); + } + } + + (done, available.len()) + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} + +fn skip_until(r: &mut R, delim: u8) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => (true, i + 1), + None => (false, available.len()), + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index 92f75e89ca048..4bd5a59e54fad 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; @@ -103,6 +104,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 95e1cc6192101..a6c9428ba62dc 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -5,7 +5,9 @@ use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::collections::VecDeque; use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; +use crate::io::{ + self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, +}; use crate::string::String; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; @@ -56,6 +58,38 @@ impl Read for &mut R { (**self).read_buf_exact(cursor) } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for &mut B { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Read for Box { @@ -177,6 +211,38 @@ impl Seek for Box { (**self).seek_relative(offset) } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Box { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} // ============================================================================= // In-memory buffer implementations @@ -311,6 +377,19 @@ impl Read for &[u8] { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} + /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] @@ -459,6 +538,26 @@ impl Read for VecDeque { unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } } } + +/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] +impl BufRead for VecDeque { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} + /// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. #[cfg(not(no_global_oom_handling))] #[stable(feature = "vecdeque_read_write", since = "1.63.0")] diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index a3fc76634d55b..ff39cb145a929 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,6 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod buf_read; mod cursor; mod error; mod impls; @@ -27,6 +28,12 @@ pub use core::io::{ stream_len_default, take, }; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{ + buf_read::BufRead, + read::{Read, read_to_string}, + util::{Bytes, Lines, Split}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ @@ -34,10 +41,5 @@ pub use self::{ DEFAULT_BUF_SIZE, append_to_string, default_read_buf, default_read_buf_exact, default_read_exact, default_read_to_end, default_read_to_string, default_read_vectored, }, - util::{SpecReadByte, bytes, uninlined_slow_read_byte}, -}; -#[unstable(feature = "alloc_io", issue = "154046")] -pub use self::{ - read::{Read, read_to_string}, - util::Bytes, + util::{SpecReadByte, bytes, lines, split, uninlined_slow_read_byte}, }; diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs index c2b82a1c25a75..050e018e8568f 100644 --- a/library/alloc/src/io/util.rs +++ b/library/alloc/src/io/util.rs @@ -1,8 +1,8 @@ use core::cmp; use crate::io::{ - self, BorrowedBuf, BorrowedCursor, Chain, Empty, IoSliceMut, Read, Repeat, Result, SizeHint, - Take, + self, BorrowedBuf, BorrowedCursor, BufRead, Chain, Empty, IoSliceMut, Read, Repeat, Result, + SizeHint, Take, }; use crate::slice; use crate::string::String; @@ -53,6 +53,37 @@ impl Read for Empty { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Empty { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(&[]) + } + + #[inline] + fn consume(&mut self, _n: usize) {} + + #[inline] + fn has_data_left(&mut self) -> io::Result { + Ok(false) + } + + #[inline] + fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn skip_until(&mut self, _byte: u8) -> io::Result { + Ok(0) + } + + #[inline] + fn read_line(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { #[inline] @@ -165,6 +196,41 @@ impl Read for Chain { } } +#[stable(feature = "chain_bufread", since = "1.9.0")] +impl BufRead for Chain { + fn fill_buf(&mut self) -> Result<&[u8]> { + if !self.done_first { + match self.first.fill_buf()? { + buf if buf.is_empty() => self.done_first = true, + buf => return Ok(buf), + } + } + self.second.fill_buf() + } + + fn consume(&mut self, amt: usize) { + if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } + } + + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + let mut read = 0; + if !self.done_first { + let n = self.first.read_until(byte, buf)?; + read += n; + + match buf.last() { + Some(b) if *b == byte && n != 0 => return Ok(read), + _ => self.done_first = true, + } + } + read += self.second.read_until(byte, buf)?; + Ok(read) + } + + // We don't override `read_line` here because an UTF-8 sequence could be + // split between the two parts of the chain +} + #[stable(feature = "rust1", since = "1.0.0")] impl Read for Take { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -238,6 +304,27 @@ impl Read for Take { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Take { + fn fill_buf(&mut self) -> Result<&[u8]> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(&[]); + } + + let buf = self.inner.fill_buf()?; + let cap = cmp::min(buf.len() as u64, self.limit) as usize; + Ok(&buf[..cap]) + } + + fn consume(&mut self, amt: usize) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt as u64, self.limit) as usize; + self.limit -= amt as u64; + self.inner.consume(amt); + } +} + /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. @@ -312,3 +399,84 @@ pub fn uninlined_slow_read_byte(reader: &mut R) -> Option> { pub const fn bytes(inner: R) -> Bytes { Bytes { inner } } + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// This struct is generally created by calling [`split`] on a `BufRead`. +/// Please see the documentation of [`split`] for more details. +/// +/// [`split`]: BufRead::split +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSplit")] +pub struct Split { + buf: B, + delim: u8, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const fn split(buf: B, delim: u8) -> Split { + Split { buf, delim } +} + +/// An iterator over the lines of an instance of [`BufRead`]. +/// +/// This struct is generally created by calling [`lines`] on a [`BufRead`]. +/// Please see the documentation of [`lines`] for more details. +/// +/// [`lines`]: BufRead::lines +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] +pub struct Lines { + buf: B, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub const fn lines(buf: B) -> Lines { + Lines { buf } +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5c7b7a266d2fc..87c2771955a9d 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -1,20 +1,2 @@ #[cfg(test)] mod tests; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc::io::Cursor; - -use crate::io::{self, BufRead}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Cursor -where - T: AsRef<[u8]>, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(Cursor::split(self).1) - } - fn consume(&mut self, amt: usize) { - self.set_position(self.position() + amt as u64); - } -} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 7329b56b2fb3f..87c2771955a9d 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -1,109 +1,2 @@ #[cfg(test)] mod tests; - -use crate::alloc::Allocator; -use crate::collections::VecDeque; -use crate::io::{self, BufRead}; - -// ============================================================================= -// Forwarding implementations - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &mut B { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Box { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -// ============================================================================= -// In-memory buffer implementations - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &[u8] { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(*self) - } - - #[inline] - fn consume(&mut self, amt: usize) { - *self = &self[amt..]; - } -} - -/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] -impl BufRead for VecDeque { - /// Returns the contents of the "front" slice as returned by - /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are - /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let (front, _) = self.as_slices(); - Ok(front) - } - - #[inline] - fn consume(&mut self, amt: usize) { - self.drain(..amt); - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d5de33a899e83..dbc92d7b45d7c 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,8 +297,6 @@ #[cfg(test)] mod tests; -use core::slice::memchr; - #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -312,8 +310,8 @@ pub use alloc_crate::io::read_to_string; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Bytes, Chain, Empty, Error, ErrorKind, Read, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, - empty, repeat, sink, + BufRead, Bytes, Chain, Cursor, Empty, Error, ErrorKind, Lines, Read, Repeat, Result, Seek, + SeekFrom, Sink, Split, Take, Write, empty, repeat, sink, }; #[allow(unused_imports, reason = "only used by certain platforms")] pub(crate) use alloc_crate::io::{ @@ -344,10 +342,8 @@ pub use self::stdio::{set_output_capture, try_set_output_capture}; pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, - cursor::Cursor, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; -use crate::cmp; mod buffered; pub(crate) mod copy; @@ -360,591 +356,3 @@ mod stdio; mod util; pub(crate) use stdio::cleanup; - -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => { - buf.extend_from_slice(&available[..=i]); - (true, i + 1) - } - None => { - buf.extend_from_slice(available); - (false, available.len()) - } - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -fn skip_until(r: &mut R, delim: u8) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => (true, i + 1), - None => (false, available.len()), - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it -/// to perform extra ways of reading. -/// -/// For example, reading line-by-line is inefficient without using a buffer, so -/// if you want to read by line, you'll need `BufRead`, which includes a -/// [`read_line`] method as well as a [`lines`] iterator. -/// -/// # Examples -/// -/// A locked standard input implements `BufRead`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// -/// let stdin = io::stdin(); -/// for line in stdin.lock().lines() { -/// println!("{}", line?); -/// } -/// # std::io::Result::Ok(()) -/// ``` -/// -/// If you have something that implements [`Read`], you can use the [`BufReader` -/// type][`BufReader`] to turn it into a `BufRead`. -/// -/// For example, [`File`] implements [`Read`], but not `BufRead`. -/// [`BufReader`] to the rescue! -/// -/// [`File`]: crate::fs::File -/// [`read_line`]: BufRead::read_line -/// [`lines`]: BufRead::lines -/// -/// ```no_run -/// use std::io::{self, BufReader}; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let f = File::open("foo.txt")?; -/// let f = BufReader::new(f); -/// -/// for line in f.lines() { -/// let line = line?; -/// println!("{line}"); -/// } -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] -pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data, via `Read` methods, if empty. - /// - /// This is a lower-level method and is meant to be used together with [`consume`], - /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. - /// - /// [`consume`]: BufRead::consume - /// - /// Returns an empty buffer when the stream has reached EOF. - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// # Examples - /// - /// A locked standard input implements `BufRead`: - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// let buffer = stdin.fill_buf()?; - /// - /// // work with buffer - /// println!("{buffer:?}"); - /// - /// // mark the bytes we worked with as read - /// let length = buffer.len(); - /// stdin.consume(length); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fill_buf(&mut self) -> Result<&[u8]>; - - /// Marks the given `amount` of additional bytes from the internal buffer as having been read. - /// Subsequent calls to `read` only return bytes that have not been marked as read. - /// - /// This is a lower-level method and is meant to be used together with [`fill_buf`], - /// which can be used to fill the internal buffer via `Read` methods. - /// - /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. - /// - /// # Examples - /// - /// Since `consume()` is meant to be used with [`fill_buf`], - /// that method's example includes an example of `consume()`. - /// - /// [`fill_buf`]: BufRead::fill_buf - #[stable(feature = "rust1", since = "1.0.0")] - fn consume(&mut self, amount: usize); - - /// Checks if there is any data left to be `read`. - /// - /// This function may fill the buffer to check for data, - /// so this function returns `Result`, not `bool`. - /// - /// The default implementation calls `fill_buf` and checks that the - /// returned slice is empty (which means that there is no data left, - /// since EOF is reached). - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// Examples - /// - /// ``` - /// #![feature(buf_read_has_data_left)] - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// while stdin.has_data_left()? { - /// let mut line = String::new(); - /// stdin.read_line(&mut line)?; - /// // work with line - /// println!("{line:?}"); - /// } - /// # std::io::Result::Ok(()) - /// ``` - #[unstable(feature = "buf_read_has_data_left", issue = "86423")] - fn has_data_left(&mut self) -> Result { - self.fill_buf().map(|b| !b.is_empty()) - } - - /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - /// - /// This function will read bytes from the underlying stream until the - /// delimiter or EOF is found. Once found, all bytes up to, and including, - /// the delimiter (if found) will be appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the bytes in a byte slice - /// in hyphen delimited segments: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); - /// let mut buf = vec![]; - /// - /// // cursor is at 'l' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 6); - /// assert_eq!(buf, b"lorem-"); - /// buf.clear(); - /// - /// // cursor is at 'i' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 5); - /// assert_eq!(buf, b"ipsum"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, b""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - read_until(self, byte, buf) - } - - /// Skips all bytes until the delimiter `byte` or EOF is reached. - /// - /// This function will read (and discard) bytes from the underlying stream until the - /// delimiter or EOF is found. - /// - /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte if found. - /// - /// This is useful for efficiently skipping data such as NUL-terminated strings - /// in binary file formats without buffering. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read some NUL-terminated information - /// about Ferris from a binary string, skipping the fun fact: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); - /// - /// // read name - /// let mut name = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut name) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 7); - /// assert_eq!(name, b"Ferris\0"); - /// - /// // skip fun fact - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 30); - /// - /// // read animal type - /// let mut animal = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut animal) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 11); - /// assert_eq!(animal, b"Crustacean\0"); - /// - /// // reach EOF - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 1); - /// ``` - #[stable(feature = "bufread_skip_until", since = "1.83.0")] - fn skip_until(&mut self, byte: u8) -> Result { - skip_until(self, byte) - } - - /// Reads all bytes until a newline (the `0xA` byte) is reached, and append - /// them to the provided `String` buffer. - /// - /// Previous content of the buffer will be preserved. To avoid appending to - /// the buffer, you need to [`clear`] it first. - /// - /// This function will read bytes from the underlying stream until the - /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes - /// up to, and including, the delimiter (if found) will be appended to - /// `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// If this function returns [`Ok(0)`], the stream has reached EOF. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending a newline - /// or EOF. You can use [`take`] to limit the maximum number of bytes read. - /// - /// [`Ok(0)`]: Ok - /// [`clear`]: String::clear - /// [`take`]: crate::io::Read::take - /// - /// # Errors - /// - /// This function has the same error semantics as [`read_until`] and will - /// also return an error if the read bytes are not valid UTF-8. If an I/O - /// error is encountered then `buf` may contain some bytes already read in - /// the event that all data read so far was valid UTF-8. - /// - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the lines in a byte slice: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"foo\nbar"); - /// let mut buf = String::new(); - /// - /// // cursor is at 'f' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 4); - /// assert_eq!(buf, "foo\n"); - /// buf.clear(); - /// - /// // cursor is at 'b' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 3); - /// assert_eq!(buf, "bar"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, ""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_line(&mut self, buf: &mut String) -> Result { - // Note that we are not calling the `.read_until` method here, but - // rather our hardcoded implementation. For more details as to why, see - // the comments in `default_read_to_string`. - unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } - } - - /// Returns an iterator over the contents of this reader split on the byte - /// `byte`. - /// - /// The iterator returned from this function will return instances of - /// [io::Result]<[Vec]\>. Each vector returned will *not* have - /// the delimiter byte at the end. - /// - /// This function will yield errors whenever [`read_until`] would have - /// also yielded an error. - /// - /// [io::Result]: self::Result "io::Result" - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all hyphen delimited - /// segments in a byte slice - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - /// - /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); - /// assert_eq!(split_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { buf: self, delim: byte } - } - - /// Returns an iterator over the lines of this reader. - /// - /// The iterator returned from this function will yield instances of - /// [io::Result]<[String]>. Each string returned will *not* have a newline - /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. - /// - /// [io::Result]: self::Result "io::Result" - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all the lines in a byte - /// slice. - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); - /// - /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); - /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); - /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); - /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); - /// assert_eq!(lines_iter.next(), None); - /// ``` - /// - /// # Errors - /// - /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. - #[stable(feature = "rust1", since = "1.0.0")] - fn lines(self) -> Lines - where - Self: Sized, - { - Lines { buf: self } - } -} - -#[stable(feature = "chain_bufread", since = "1.9.0")] -impl BufRead for Chain { - fn fill_buf(&mut self) -> Result<&[u8]> { - if !self.done_first { - match self.first.fill_buf()? { - buf if buf.is_empty() => self.done_first = true, - buf => return Ok(buf), - } - } - self.second.fill_buf() - } - - fn consume(&mut self, amt: usize) { - if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } - } - - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - let mut read = 0; - if !self.done_first { - let n = self.first.read_until(byte, buf)?; - read += n; - - match buf.last() { - Some(b) if *b == byte && n != 0 => return Ok(read), - _ => self.done_first = true, - } - } - read += self.second.read_until(byte, buf)?; - Ok(read) - } - - // We don't override `read_line` here because an UTF-8 sequence could be - // split between the two parts of the chain -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Take { - fn fill_buf(&mut self) -> Result<&[u8]> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(&[]); - } - - let buf = self.inner.fill_buf()?; - let cap = cmp::min(buf.len() as u64, self.limit) as usize; - Ok(&buf[..cap]) - } - - fn consume(&mut self, amt: usize) { - // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; - self.inner.consume(amt); - } -} - -/// An iterator over the contents of an instance of `BufRead` split on a -/// particular byte. -/// -/// This struct is generally created by calling [`split`] on a `BufRead`. -/// Please see the documentation of [`split`] for more details. -/// -/// [`split`]: BufRead::split -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSplit")] -pub struct Split { - buf: B, - delim: u8, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Split { - type Item = Result>; - - fn next(&mut self) -> Option>> { - let mut buf = Vec::new(); - match self.buf.read_until(self.delim, &mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf[buf.len() - 1] == self.delim { - buf.pop(); - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} - -/// An iterator over the lines of an instance of `BufRead`. -/// -/// This struct is generally created by calling [`lines`] on a `BufRead`. -/// Please see the documentation of [`lines`] for more details. -/// -/// [`lines`]: BufRead::lines -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] -pub struct Lines { - buf: B, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Lines { - type Item = Result; - - fn next(&mut self) -> Option> { - let mut buf = String::new(); - match self.buf.read_line(&mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 936727f5fbc62..87c2771955a9d 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -1,37 +1,2 @@ -#![allow(missing_copy_implementations)] - #[cfg(test)] mod tests; - -use crate::io::{self, BufRead, Empty}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Empty { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(&[]) - } - - #[inline] - fn consume(&mut self, _n: usize) {} - - #[inline] - fn has_data_left(&mut self) -> io::Result { - Ok(false) - } - - #[inline] - fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { - Ok(0) - } - - #[inline] - fn skip_until(&mut self, _byte: u8) -> io::Result { - Ok(0) - } - - #[inline] - fn read_line(&mut self, _buf: &mut String) -> io::Result { - Ok(0) - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index e0dfa3b7988e8..229bbeaab0cee 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -397,6 +397,7 @@ // tidy-alphabetical-start #![feature(alloc_io)] #![feature(allocator_api)] +#![feature(buf_read_has_data_left)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] diff --git a/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html b/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html index e8ab061e679dd..8e9465ef6126b 100644 --- a/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html +++ b/tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html @@ -5,7 +5,7 @@ // Provided methods fn func(self) where Self: Sized { ... } - fn lines(self) -> Lines<Self> + fn lines(self) -> Lines<Self> where Self: Sized { ... } fn merge<T>(self, a: T) where Self: Sized, From 82b55bac74fef996335ecba745a9b4c7ac074dfc Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:29:26 +1000 Subject: [PATCH 14/14] Fix documentation links to `BufRead` --- library/alloc/src/io/read.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index b030663818c72..0d8343771877a 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -22,8 +22,10 @@ use crate::vec::Vec; /// trait. /// /// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements `BufRead`, such as /// `BufReader`, will be more efficient. +/// therefore, using something that implements [`BufRead`], such as +/// +/// [`BufRead`]: crate::io::BufRead /// /// Repeated calls to the reader use the same cursor, so for example /// calling `read_to_end` twice on a `File` will only return the file's