From e33662abd6fb74a7a6ca2294d407ea952b3d8c95 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 11:09:23 +1000 Subject: [PATCH 1/3] 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 c35f121554c1c..a2582355d6794 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -399,6 +399,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 982072a7c744126112b40205c8627009f34b3ba1 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:29:26 +1000 Subject: [PATCH 2/3] 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 c6b802ef9862a..c1356811cc58a 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -22,8 +22,10 @@ use crate::vec::Vec; /// trait. /// /// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements `BufRead`, such as /// `BufReader`, will be more efficient. +/// therefore, using something that implements [`BufRead`], such as +/// +/// [`BufRead`]: crate::io::BufRead /// /// Repeated calls to the reader use the same cursor, so for example /// calling `read_to_end` twice on a `File` will only return the file's From 7c0c4efec8a13e7c5cd7ff26f08014c64da005b0 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Sun, 19 Jul 2026 12:14:27 +1000 Subject: [PATCH 3/3] Prepend `read_until` and `skip_until` with `default_` More consistent with the default functions for `Read` Co-Authored-By: Clar Fon <15850505+clarfonthey@users.noreply.github.com> --- library/alloc/src/io/buf_read.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/io/buf_read.rs b/library/alloc/src/io/buf_read.rs index c4467fcb495e3..bba1c2b8c8a45 100644 --- a/library/alloc/src/io/buf_read.rs +++ b/library/alloc/src/io/buf_read.rs @@ -203,7 +203,7 @@ pub trait BufRead: Read { /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - read_until(self, byte, buf) + default_read_until(self, byte, buf) } /// Skips all bytes until the delimiter `byte` or EOF is reached. @@ -270,7 +270,7 @@ pub trait BufRead: Read { /// ``` #[stable(feature = "bufread_skip_until", since = "1.83.0")] fn skip_until(&mut self, byte: u8) -> Result { - skip_until(self, byte) + default_skip_until(self, byte) } /// Reads all bytes until a newline (the `0xA` byte) is reached, and append @@ -343,7 +343,7 @@ pub trait BufRead: Read { // 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)) } + unsafe { append_to_string(buf, |b| default_read_until(self, b'\n', b)) } } /// Returns an iterator over the contents of this reader split on the byte @@ -426,7 +426,11 @@ pub trait BufRead: Read { } } -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { +fn default_read_until( + r: &mut R, + delim: u8, + buf: &mut Vec, +) -> Result { let mut read = 0; loop { let (done, used) = { @@ -473,7 +477,7 @@ fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> R } } -fn skip_until(r: &mut R, delim: u8) -> Result { +fn default_skip_until(r: &mut R, delim: u8) -> Result { let mut read = 0; loop { let (done, used) = {