diff options
Diffstat (limited to 'library/std/src/io')
| -rw-r--r-- | library/std/src/io/cursor.rs | 54 | ||||
| -rw-r--r-- | library/std/src/io/error.rs | 4 | ||||
| -rw-r--r-- | library/std/src/io/impls.rs | 19 | ||||
| -rw-r--r-- | library/std/src/io/mod.rs | 16 | ||||
| -rw-r--r-- | library/std/src/io/stdio.rs | 34 | ||||
| -rw-r--r-- | library/std/src/io/tests.rs | 32 | ||||
| -rw-r--r-- | library/std/src/io/util.rs | 6 |
7 files changed, 132 insertions, 33 deletions
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5b782fff7e5..a1a8b2a3505 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -95,7 +95,7 @@ impl<T> Cursor<T> { /// # force_inference(&buff); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn new(inner: T) -> Cursor<T> { Cursor { pos: 0, inner } } @@ -132,7 +132,7 @@ impl<T> Cursor<T> { /// let reference = buff.get_ref(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn get_ref(&self) -> &T { &self.inner } @@ -178,7 +178,7 @@ impl<T> Cursor<T> { /// assert_eq!(buff.position(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn position(&self) -> u64 { self.pos } @@ -328,7 +328,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?; + Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,17 +352,45 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let n = buf.len(); - Read::read_exact(&mut self.remaining_slice(), buf)?; - self.pos += n as u64; - Ok(()) + let result = Read::read_exact(&mut self.remaining_slice(), buf); + + match result { + Ok(_) => self.pos += buf.len() as u64, + // The only possible error condition is EOF, so place the cursor at "EOF" + Err(_) => self.pos = self.inner.as_ref().len() as u64, + } + + result } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let n = cursor.capacity(); - Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; - self.pos += n as u64; - Ok(()) + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + self.pos += (cursor.written() - prev_written) as u64; + + result + } + + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + let content = self.remaining_slice(); + let len = content.len(); + buf.try_reserve(len)?; + buf.extend_from_slice(content); + self.pos += len as u64; + + Ok(len) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { + let content = + crate::str::from_utf8(self.remaining_slice()).map_err(|_| io::Error::INVALID_UTF8)?; + let len = content.len(); + buf.try_reserve(len)?; + buf.push_str(content); + self.pos += len as u64; + + Ok(len) } } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 21ae7b91207..f366cb8f42b 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -869,8 +869,6 @@ impl Error { /// # Examples /// /// ``` - /// #![feature(io_error_downcast)] - /// /// use std::fmt; /// use std::io; /// use std::error::Error; @@ -923,7 +921,7 @@ impl Error { /// assert!(io_error.raw_os_error().is_none()); /// # } /// ``` - #[unstable(feature = "io_error_downcast", issue = "99262")] + #[stable(feature = "io_error_downcast", since = "1.79.0")] pub fn downcast<E>(self) -> result::Result<E, Self> where E: error::Error + Send + Sync + 'static, diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index dd7e0725176..a8a2e9413e1 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -287,6 +287,9 @@ impl Read for &[u8] { #[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()); @@ -307,6 +310,9 @@ impl Read for &[u8] { #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(cursor.capacity()); @@ -329,8 +335,9 @@ impl Read for &[u8] { #[inline] fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; - buf.push_str(content); let len = self.len(); + buf.try_reserve(len)?; + buf.push_str(content); *self = &self[len..]; Ok(len) } @@ -473,14 +480,8 @@ impl<A: Allocator> Read for VecDeque<u8, A> { #[inline] fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { - // We have to use a single contiguous slice because the `VecDequeue` might be split in the - // middle of an UTF-8 character. - let len = self.len(); - let content = self.make_contiguous(); - let string = str::from_utf8(content).map_err(|_| io::Error::INVALID_UTF8)?; - buf.push_str(string); - self.clear(); - Ok(len) + // SAFETY: We only append to the buffer + unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } } } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 98973a43e1d..f55ec1588f9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -266,7 +266,7 @@ //! its file descriptors with no operations being performed by any other part of the program. //! //! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the -//! underlying kernel object that the file descriptor references (also called "file description" on +//! underlying kernel object that the file descriptor references (also called "open file description" on //! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned //! file descriptor, you cannot know whether there are any other file descriptors that reference the //! same kernel object. However, when you create a new kernel object, you know that you are holding @@ -384,7 +384,10 @@ where { let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; let ret = f(g.buf); - if str::from_utf8(&g.buf[g.len..]).is_err() { + + // SAFETY: the caller promises to only append data to `buf` + let appended = 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(); @@ -1836,7 +1839,11 @@ pub trait Write { if output.error.is_err() { output.error } else { - Err(error::const_io_error!(ErrorKind::Uncategorized, "formatter error")) + // 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" + ); } } } @@ -2037,7 +2044,6 @@ pub trait Seek { /// # Example /// /// ```no_run - /// #![feature(seek_seek_relative)] /// use std::{ /// io::{self, Seek}, /// fs::File, @@ -2052,7 +2058,7 @@ pub trait Seek { /// ``` /// /// [`BufReader`]: crate::io::BufReader - #[unstable(feature = "seek_seek_relative", issue = "117374")] + #[stable(feature = "seek_seek_relative", since = "CURRENT_RUSTC_VERSION")] fn seek_relative(&mut self, offset: i64) -> Result<()> { self.seek(SeekFrom::Current(offset))?; Ok(()) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 07fa9259e0b..c8968b74b12 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -1161,7 +1161,41 @@ pub trait IsTerminal: crate::sealed::Sealed { /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. /// Note that this [may change in the future][changes]. /// + /// # Examples + /// + /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: + /// + /// ```no_run + /// use std::io::{self, IsTerminal, Write}; + /// + /// fn main() -> io::Result<()> { + /// let stdin = io::stdin(); + /// + /// // Indicate that the user is prompted for input, if this is a terminal. + /// if stdin.is_terminal() { + /// print!("> "); + /// io::stdout().flush()?; + /// } + /// + /// let mut name = String::new(); + /// let _ = stdin.read_line(&mut name)?; + /// + /// println!("Hello {}", name.trim_end()); + /// + /// Ok(()) + /// } + /// ``` + /// + /// The example can be run in two ways: + /// + /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` + /// it will print: `Hello foo`. + /// - If you instead run the example interactively by running the executable directly, it will + /// panic with the message "Expected input to be piped to the process". + /// + /// /// [changes]: io#platform-specific-behavior + /// [`Stdin`]: crate::io::Stdin #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 090a091b09a..a2c1c430863 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -653,6 +653,38 @@ fn test_take_wrong_length() { let _ = reader.read(&mut buffer[..]); } +#[test] +fn slice_read_exact_eof() { + let slice = &b"123456"[..]; + + let mut r = slice; + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[test] +fn cursor_read_exact_eof() { + let slice = Cursor::new(b"123456"); + + let mut r = slice.clone(); + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + #[bench] fn bench_take_read(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 266a447f06b..b4c4dffc371 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -51,7 +51,7 @@ pub struct Empty; /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn empty() -> Empty { Empty } @@ -173,7 +173,7 @@ pub struct Repeat { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn repeat(byte: u8) -> Repeat { Repeat { byte } } @@ -276,7 +276,7 @@ pub struct Sink; /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn sink() -> Sink { Sink } |
