1
Fork 0

Implement default methods for io::Empty and io::Sink

Eliminate any redundant, unobservable logic from the their default
method implementations.

The observable changes are that `Write::write_fmt` for both types now
ignores the formatting arguments, so a user fmt impl which has side
effects is not invoked, and `Write::write_all_vectored` for both types
does not advance the borrowed buffers. Neither behavior is guaranteed by
the docs and the latter is documented as unspecified.

`Empty` is not marked as vectored, so that `Chain<Empty, _>` and
`Chain<_, Empty>` are not forced to be vectored.
This commit is contained in:
Thalia Archibald 2025-02-14 13:40:24 -08:00
parent 2c6a12ec44
commit 523b9d9428
3 changed files with 218 additions and 13 deletions

View file

@ -68,6 +68,38 @@ impl Read for Empty {
fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
Ok(())
}
#[inline]
fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
Ok(0)
}
#[inline]
fn is_read_vectored(&self) -> bool {
// Do not force `Chain<Empty, T>` or `Chain<T, Empty>` to use vectored
// reads, unless the other reader is vectored.
false
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
}
#[inline]
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
}
#[inline]
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Ok(0)
}
#[inline]
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Ok(0)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl BufRead for Empty {
@ -75,20 +107,44 @@ impl BufRead for Empty {
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<bool> {
Ok(false)
}
#[inline]
fn read_until(&mut self, _byte: u8, _buf: &mut Vec<u8>) -> io::Result<usize> {
Ok(0)
}
#[inline]
fn skip_until(&mut self, _byte: u8) -> io::Result<usize> {
Ok(0)
}
#[inline]
fn read_line(&mut self, _buf: &mut String) -> io::Result<usize> {
Ok(0)
}
}
#[stable(feature = "empty_seek", since = "1.51.0")]
impl Seek for Empty {
#[inline]
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
Ok(0)
}
#[inline]
fn stream_len(&mut self) -> io::Result<u64> {
Ok(0)
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
Ok(0)
}
@ -119,6 +175,21 @@ impl Write for Empty {
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(())
@ -143,6 +214,21 @@ impl Write for &Empty {
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(())
@ -302,6 +388,21 @@ impl Write for Sink {
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(())
@ -326,6 +427,21 @@ impl Write for &Sink {
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(())

View file

@ -1,14 +1,51 @@
use crate::fmt;
use crate::io::prelude::*;
use crate::io::{BorrowedBuf, Empty, Repeat, SeekFrom, Sink, empty, repeat, sink};
use crate::io::{
BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink,
};
use crate::mem::MaybeUninit;
struct ErrorDisplay;
impl fmt::Display for ErrorDisplay {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Err(fmt::Error)
}
}
struct PanicDisplay;
impl fmt::Display for PanicDisplay {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
panic!()
}
}
#[track_caller]
fn test_sinking<W: Write>(mut w: W) {
assert_eq!(w.write(&[]).unwrap(), 0);
assert_eq!(w.write(&[0]).unwrap(), 1);
assert_eq!(w.write(&[0; 1024]).unwrap(), 1024);
w.write_all(&[]).unwrap();
w.write_all(&[0]).unwrap();
w.write_all(&[0; 1024]).unwrap();
let mut bufs =
[IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])];
assert!(w.is_write_vectored());
assert_eq!(w.write_vectored(&[]).unwrap(), 0);
assert_eq!(w.write_vectored(&bufs).unwrap(), 1025);
w.write_all_vectored(&mut []).unwrap();
w.write_all_vectored(&mut bufs).unwrap();
assert!(w.flush().is_ok());
assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024);
// Ignores fmt arguments
w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap();
w.write_fmt(format_args!("{}", PanicDisplay)).unwrap();
}
#[test]
fn sink_sinks() {
let mut s = sink();
assert_eq!(s.write(&[]).unwrap(), 0);
assert_eq!(s.write(&[0]).unwrap(), 1);
assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
test_sinking(sink());
}
#[test]
@ -19,6 +56,21 @@ fn empty_reads() {
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
e.read_exact(&mut []).unwrap();
assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert!(!e.is_read_vectored());
assert_eq!(e.read_vectored(&mut []).unwrap(), 0);
let (mut buf1, mut buf1024) = ([0], [0; 1024]);
let bufs = &mut [
IoSliceMut::new(&mut []),
IoSliceMut::new(&mut buf1),
IoSliceMut::new(&mut buf1024),
IoSliceMut::new(&mut []),
];
assert_eq!(e.read_vectored(bufs).unwrap(), 0);
let buf: &mut [MaybeUninit<_>] = &mut [];
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf(buf.unfilled()).unwrap();
@ -42,6 +94,47 @@ fn empty_reads() {
Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
let buf: &mut [MaybeUninit<_>] = &mut [];
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf_exact(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
let mut buf: BorrowedBuf<'_> = buf.into();
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
assert_eq!(
Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(),
ErrorKind::UnexpectedEof,
);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
let mut buf = Vec::new();
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
assert_eq!(buf, vec![]);
let mut buf = vec![1, 2, 3];
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
assert_eq!(buf, vec![1, 2, 3]);
let mut buf = String::new();
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
assert_eq!(buf, "");
let mut buf = "hello".to_owned();
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
assert_eq!(buf, "hello");
}
#[test]
@ -66,11 +159,7 @@ fn empty_seeks() {
#[test]
fn empty_sinks() {
let mut e = empty();
assert_eq!(e.write(&[]).unwrap(), 0);
assert_eq!(e.write(&[0]).unwrap(), 1);
assert_eq!(e.write(&[0; 1024]).unwrap(), 1024);
assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024);
test_sinking(empty());
}
#[test]

View file

@ -4,7 +4,7 @@
#![feature(io_error_uncategorized)]
use std::fmt;
use std::io::{self, Error, Write, sink};
use std::io::{self, Error, Write};
use std::panic::catch_unwind;
struct ErrorDisplay;
@ -33,7 +33,7 @@ fn main() {
assert!(res.is_err(), "writer error did not propagate");
// Test that the error from the formatter is detected.
let res = catch_unwind(|| write!(sink(), "{} {} {}", 1, ErrorDisplay, "bar"));
let res = catch_unwind(|| write!(vec![], "{} {} {}", 1, ErrorDisplay, "bar"));
let err = res.expect_err("formatter error did not lead to panic").downcast::<&str>().unwrap();
assert!(
err.contains("formatting trait implementation returned an error"),