Rollup merge of #137051 - thaliaarchi:io-optional-impls/empty, r=m-ou-se
Implement default methods for `io::Empty` and `io::Sink` Implements default methods of `io::Read`, `io::BufRead`, and `io::Write` for `io::Empty` and `io::Sink`. These implementations are equivalent to the defaults, except in doing less unnecessary work. `Read::read_to_string` and `BufRead::read_line` both have a redundant call to `str::from_utf8` which can't be inlined from `core` and `Write::write_all_vectored` has slicing logic which can't be simplified (See on [Compiler Explorer](https://rust.godbolt.org/z/KK6xcrWr4)). The rest are optimized to the minimal with `-C opt-level=3`, but this PR gives that benefit to unoptimized builds. This includes an implementation of `Write::write_fmt` which just ignores the `fmt::Arguments<'_>`. This could be problematic whenever a user formatting impl is impure, but the docs do not guarantee that the args will be expanded. Tracked in https://github.com/rust-lang/rust/issues/136756. r? `@m-ou-se`
This commit is contained in:
commit
ce76292014
3 changed files with 218 additions and 13 deletions
|
@ -67,6 +67,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 {
|
||||
|
@ -74,20 +106,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)
|
||||
}
|
||||
|
@ -118,6 +174,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(())
|
||||
|
@ -142,6 +213,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(())
|
||||
|
@ -301,6 +387,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(())
|
||||
|
@ -325,6 +426,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(())
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue