diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs new file mode 100644 index 0000000000000..6f392bba93ef4 --- /dev/null +++ b/library/alloc/src/io/impls.rs @@ -0,0 +1,22 @@ +use crate::boxed::Box; +use crate::io::SizeHint; + +// ============================================================================= +// Forwarding implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Box { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&**self) + } + + #[inline] + fn upper_bound(&self) -> Option { + SizeHint::upper_bound(&**self) + } +} + +// ============================================================================= +// In-memory buffer implementations diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 58e5ffa39d0c4..86dc8c685a985 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,6 +1,7 @@ //! Traits, helpers, and type definitions for core I/O functionality. mod error; +mod impls; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; @@ -17,4 +18,4 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{OsFunctions, chain, take}; +pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take}; diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs new file mode 100644 index 0000000000000..cc222c56121e2 --- /dev/null +++ b/library/core/src/io/impls.rs @@ -0,0 +1,35 @@ +use crate::io::SizeHint; + +// ============================================================================= +// Forwarding implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for &mut T { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(*self) + } + + #[inline] + fn upper_bound(&self) -> Option { + SizeHint::upper_bound(*self) + } +} + +// ============================================================================= +// In-memory buffer implementations + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for &[u8] { + #[inline] + fn lower_bound(&self) -> usize { + self.len() + } + + #[inline] + fn upper_bound(&self) -> Option { + Some(self.len()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index b3c8815cb062c..95fcc2d6dc19b 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -3,7 +3,9 @@ mod borrowed_buf; mod cursor; mod error; +mod impls; mod io_slice; +mod size_hint; mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] @@ -25,5 +27,26 @@ pub use self::{ #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ error::{Custom, CustomOwner, OsFunctions}, + size_hint::SizeHint, util::{chain, take}, }; + +/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically +/// implemented for handle types like [`Arc`][arc] as well. +/// +/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)` +/// would be identical to `::method(&mut value, ..)`. +/// +/// [`File`][file] passes this test, as operations on `&File` and `File` both affect +/// the same underlying file. +/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary +/// and be lost after the method has been called. +/// +// 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 +/// [`Seek`]: ../../std/io/trait.Seek.html +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait IoHandle {} diff --git a/library/core/src/io/size_hint.rs b/library/core/src/io/size_hint.rs new file mode 100644 index 0000000000000..fa4e25c62f6d0 --- /dev/null +++ b/library/core/src/io/size_hint.rs @@ -0,0 +1,60 @@ +/// Internal trait used to allow for specialization in `Read::size_hint` and +/// `Iterator::size_hint`. +/// +/// All types implement this through a blanket default implementation returning +/// a hint of `(0, None)`. +/// +/// Implementors should only provide [`lower_bound`](SizeHint::lower_bound) and +/// [`upper_bound`](SizeHint::upper_bound). +/// [`size_hint`](SizeHint::size_hint) is provided as a `final` method to enforce +/// correctness. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait SizeHint { + /// Returns a lower bound on the number of elements this container-like item + /// contains. + /// For example, an array `[u8; 12]` could return any value between `0` and + /// `12` inclusively as a correct implementation. + /// + /// Through specialization, all types implement this method returning a default + /// value of `0`. + /// + /// Implementations *must* ensure the returned value is less than or equal to + /// the true element count. + fn lower_bound(&self) -> usize; + + /// Returns an upper bound on the number of elements this container-like item + /// contains if it can be determined, otherwise `None`. + /// + /// Through specialization, all types implement this method returning a default + /// value of `None`. + /// + /// Implementations *must* ensure the returned value is greater than or equal + /// to the true element count. + fn upper_bound(&self) -> Option; + + /// Returns an estimate for the number of elements this container like type + /// contains. + /// + /// This is a `final` method, and is guaranteed to return + /// `(self.lower_bound(), self.upper_bound())`. + /// + /// Without specialization, types implementing this trait will return `(0, None)`. + final fn size_hint(&self) -> (usize, Option) { + (self.lower_bound(), self.upper_bound()) + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for T { + #[inline] + default fn lower_bound(&self) -> usize { + 0 + } + + #[inline] + default fn upper_bound(&self) -> Option { + None + } +} diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index 232e388433fb0..a40318bb85e08 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,5 @@ -use crate::fmt; +use crate::io::SizeHint; +use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. @@ -13,6 +14,15 @@ use crate::fmt; #[derive(Copy, Clone, Debug, Default)] pub struct Empty; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Empty { + #[inline] + fn upper_bound(&self) -> Option { + Some(0) + } +} + /// Creates a value that is always at EOF for reads, and ignores all data written. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] @@ -63,6 +73,20 @@ pub struct Repeat { pub byte: u8, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Repeat { + #[inline] + fn lower_bound(&self) -> usize { + usize::MAX + } + + #[inline] + fn upper_bound(&self) -> Option { + None + } +} + /// Creates an instance of a reader that infinitely repeats one byte. /// /// All reads from this reader will succeed by filling the specified buffer with @@ -145,6 +169,23 @@ pub struct Chain { pub done_first: bool, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Chain { + #[inline] + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) + } + + #[inline] + fn upper_bound(&self) -> Option { + match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { + (Some(first), Some(second)) => first.checked_add(second), + _ => None, + } + } +} + impl Chain { /// Consumes the `Chain`, returning the wrapped readers. /// @@ -253,6 +294,23 @@ pub struct Take { pub limit: u64, } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl SizeHint for Take { + #[inline] + fn lower_bound(&self) -> usize { + cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize + } + + #[inline] + fn upper_bound(&self) -> Option { + match SizeHint::upper_bound(&self.inner) { + Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize), + None => self.limit.try_into().ok(), + } + } +} + impl Take { /// Returns the number of bytes that can be read before this instance will /// return EOF. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 750ddb91c482b..77be07b097b03 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1540,6 +1540,8 @@ impl Seek for File { (&*self).stream_position() } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl crate::io::IoHandle for File {} impl Dir { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 4d9eecb0d9648..075ea44377c78 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -580,6 +580,8 @@ impl Seek for BufReader { } } +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] impl SizeHint for BufReader { #[inline] fn lower_bound(&self) -> usize { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index dc623a3e82c2c..b06ec9af7239d 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,7 +299,7 @@ mod tests; use core::slice::memchr; -use alloc_crate::io::OsFunctions; +pub(crate) use alloc_crate::io::IoHandle; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -315,6 +315,7 @@ pub use alloc_crate::io::{ }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; +use alloc_crate::io::{OsFunctions, SizeHint}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; @@ -1980,21 +1981,6 @@ pub enum SeekFrom { Current(#[stable(feature = "rust1", since = "1.0.0")] i64), } -/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically -/// implemented for handle types like [`Arc`][arc] as well. -/// -/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)` -/// would be identical to `::method(&mut value, ..)`. -/// -/// [`File`][file] passes this test, as operations on `&File` and `File` both affect -/// the same underlying file. -/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary -/// and be lost after the method has been called. -/// -/// [file]: crate::fs::File -/// [arc]: crate::sync::Arc -pub(crate) trait IoHandle {} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { @@ -2552,21 +2538,6 @@ impl BufRead for Chain { // split between the two parts of the chain } -impl SizeHint for Chain { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) - } - - #[inline] - fn upper_bound(&self) -> Option { - match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { - (Some(first), Some(second)) => first.checked_add(second), - _ => None, - } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Take { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -2661,21 +2632,6 @@ impl BufRead for Take { } } -impl SizeHint for Take { - #[inline] - fn lower_bound(&self) -> usize { - cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize - } - - #[inline] - fn upper_bound(&self) -> Option { - match SizeHint::upper_bound(&self.inner) { - Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize), - None => self.limit.try_into().ok(), - } - } -} - #[stable(feature = "seek_io_take", since = "1.89.0")] impl Seek for Take { fn seek(&mut self, pos: SeekFrom) -> Result { @@ -2784,64 +2740,6 @@ fn uninlined_slow_read_byte(reader: &mut R) -> Option> { inlined_slow_read_byte(reader) } -trait SizeHint { - fn lower_bound(&self) -> usize; - - fn upper_bound(&self) -> Option; - - fn size_hint(&self) -> (usize, Option) { - (self.lower_bound(), self.upper_bound()) - } -} - -impl SizeHint for T { - #[inline] - default fn lower_bound(&self) -> usize { - 0 - } - - #[inline] - default fn upper_bound(&self) -> Option { - None - } -} - -impl SizeHint for &mut T { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(*self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(*self) - } -} - -impl SizeHint for Box { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&**self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(&**self) - } -} - -impl SizeHint for &[u8] { - #[inline] - fn lower_bound(&self) -> usize { - self.len() - } - - #[inline] - fn upper_bound(&self) -> Option { - Some(self.len()) - } -} - /// An iterator over the contents of an instance of `BufRead` split on a /// particular byte. /// diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index dbda72a43a637..fbb69a11c6b92 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -6,7 +6,7 @@ mod tests; use crate::fmt; use crate::io::{ self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, - SizeHint, Write, + Write, }; #[stable(feature = "rust1", since = "1.0.0")] @@ -102,13 +102,6 @@ impl Seek for Empty { } } -impl SizeHint for Empty { - #[inline] - fn upper_bound(&self) -> Option { - Some(0) - } -} - #[stable(feature = "empty_write", since = "1.73.0")] impl Write for Empty { #[inline] @@ -240,18 +233,6 @@ impl Read for Repeat { } } -impl SizeHint for Repeat { - #[inline] - fn lower_bound(&self) -> usize { - usize::MAX - } - - #[inline] - fn upper_bound(&self) -> Option { - None - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Write for Sink { #[inline]