Auto merge of #136769 - thaliaarchi:io-optional-methods/stdio, r=joboet

Provide optional `Read`/`Write` methods for stdio

Override more of the default methods for `io::Read` and `io::Write` for stdio types, when efficient to do so, and deduplicate unsupported types.

Tracked in https://github.com/rust-lang/rust/issues/136756.

try-job: x86_64-msvc-1
This commit is contained in:
bors 2025-03-23 06:23:51 +00:00
commit 60a3084f64
7 changed files with 138 additions and 163 deletions

View File

@ -97,15 +97,15 @@ const fn stderr_raw() -> StderrRaw {
impl Read for StdinRaw {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
handle_ebadf(self.0.read(buf), 0)
handle_ebadf(self.0.read(buf), || Ok(0))
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
handle_ebadf(self.0.read_buf(buf), ())
handle_ebadf(self.0.read_buf(buf), || Ok(()))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
handle_ebadf(self.0.read_vectored(bufs), 0)
handle_ebadf(self.0.read_vectored(bufs), || Ok(0))
}
#[inline]
@ -113,23 +113,37 @@ impl Read for StdinRaw {
self.0.is_read_vectored()
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if buf.is_empty() {
return Ok(());
}
handle_ebadf(self.0.read_exact(buf), || Err(io::Error::READ_EXACT_EOF))
}
fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
if buf.capacity() == 0 {
return Ok(());
}
handle_ebadf(self.0.read_buf_exact(buf), || Err(io::Error::READ_EXACT_EOF))
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
handle_ebadf(self.0.read_to_end(buf), 0)
handle_ebadf(self.0.read_to_end(buf), || Ok(0))
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
handle_ebadf(self.0.read_to_string(buf), 0)
handle_ebadf(self.0.read_to_string(buf), || Ok(0))
}
}
impl Write for StdoutRaw {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
handle_ebadf(self.0.write(buf), buf.len())
handle_ebadf(self.0.write(buf), || Ok(buf.len()))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let total = || bufs.iter().map(|b| b.len()).sum();
handle_ebadf_lazy(self.0.write_vectored(bufs), total)
let total = || Ok(bufs.iter().map(|b| b.len()).sum());
handle_ebadf(self.0.write_vectored(bufs), total)
}
#[inline]
@ -138,30 +152,30 @@ impl Write for StdoutRaw {
}
fn flush(&mut self) -> io::Result<()> {
handle_ebadf(self.0.flush(), ())
handle_ebadf(self.0.flush(), || Ok(()))
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
handle_ebadf(self.0.write_all(buf), ())
handle_ebadf(self.0.write_all(buf), || Ok(()))
}
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
handle_ebadf(self.0.write_all_vectored(bufs), ())
handle_ebadf(self.0.write_all_vectored(bufs), || Ok(()))
}
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
handle_ebadf(self.0.write_fmt(fmt), ())
handle_ebadf(self.0.write_fmt(fmt), || Ok(()))
}
}
impl Write for StderrRaw {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
handle_ebadf(self.0.write(buf), buf.len())
handle_ebadf(self.0.write(buf), || Ok(buf.len()))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let total = || bufs.iter().map(|b| b.len()).sum();
handle_ebadf_lazy(self.0.write_vectored(bufs), total)
let total = || Ok(bufs.iter().map(|b| b.len()).sum());
handle_ebadf(self.0.write_vectored(bufs), total)
}
#[inline]
@ -170,32 +184,25 @@ impl Write for StderrRaw {
}
fn flush(&mut self) -> io::Result<()> {
handle_ebadf(self.0.flush(), ())
handle_ebadf(self.0.flush(), || Ok(()))
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
handle_ebadf(self.0.write_all(buf), ())
handle_ebadf(self.0.write_all(buf), || Ok(()))
}
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
handle_ebadf(self.0.write_all_vectored(bufs), ())
handle_ebadf(self.0.write_all_vectored(bufs), || Ok(()))
}
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
handle_ebadf(self.0.write_fmt(fmt), ())
handle_ebadf(self.0.write_fmt(fmt), || Ok(()))
}
}
fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
fn handle_ebadf<T>(r: io::Result<T>, default: impl FnOnce() -> io::Result<T>) -> io::Result<T> {
match r {
Err(ref e) if stdio::is_ebadf(e) => Ok(default),
r => r,
}
}
fn handle_ebadf_lazy<T>(r: io::Result<T>, default: impl FnOnce() -> T) -> io::Result<T> {
match r {
Err(ref e) if stdio::is_ebadf(e) => Ok(default()),
Err(ref e) if stdio::is_ebadf(e) => default(),
r => r,
}
}

View File

@ -3,9 +3,9 @@ use fortanix_sgx_abi as abi;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::sys::fd::FileDesc;
pub struct Stdin(());
pub struct Stdout(());
pub struct Stderr(());
pub struct Stdin;
pub struct Stdout;
pub struct Stderr;
fn with_std_fd<F: FnOnce(&FileDesc) -> R, R>(fd: abi::Fd, f: F) -> R {
let fd = FileDesc::new(fd);
@ -16,7 +16,7 @@ fn with_std_fd<F: FnOnce(&FileDesc) -> R, R>(fd: abi::Fd, f: F) -> R {
impl Stdin {
pub const fn new() -> Stdin {
Stdin(())
Stdin
}
}
@ -41,7 +41,7 @@ impl io::Read for Stdin {
impl Stdout {
pub const fn new() -> Stdout {
Stdout(())
Stdout
}
}
@ -66,7 +66,7 @@ impl io::Write for Stdout {
impl Stderr {
pub const fn new() -> Stderr {
Stderr(())
Stderr
}
}

View File

@ -1,22 +1,13 @@
#[expect(dead_code)]
#[path = "unsupported.rs"]
mod unsupported_stdio;
use crate::io;
use crate::sys::pal::abi;
pub struct Stdin;
pub type Stdin = unsupported_stdio::Stdin;
pub struct Stdout;
pub struct Stderr;
struct PanicOutput;
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
}
pub type Stderr = Stdout;
impl Stdout {
pub const fn new() -> Stdout {
@ -35,46 +26,12 @@ impl io::Write for Stdout {
}
}
impl Stderr {
pub const fn new() -> Stderr {
Stderr
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl PanicOutput {
pub const fn new() -> PanicOutput {
PanicOutput
}
}
impl io::Write for PanicOutput {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE;
pub fn is_ebadf(_err: &io::Error) -> bool {
true
}
pub fn panic_output() -> Option<impl io::Write> {
Some(PanicOutput::new())
Some(Stderr::new())
}

View File

@ -1,12 +1,16 @@
#![deny(unsafe_op_in_unsafe_fn)]
#[expect(dead_code)]
#[path = "unsupported.rs"]
mod unsupported_stdio;
use core::arch::asm;
use crate::io;
pub struct Stdin;
pub type Stdin = unsupported_stdio::Stdin;
pub struct Stdout;
pub struct Stderr;
pub type Stderr = Stdout;
const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2;
@ -25,27 +29,6 @@ unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 {
ret as i32
}
fn print_buf(s: &[u8]) -> io::Result<usize> {
// Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`.
const MAX_LEN: usize = 512;
let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() };
let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) };
if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) }
}
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
}
impl Stdout {
pub const fn new() -> Stdout {
Stdout
@ -54,7 +37,13 @@ impl Stdout {
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
print_buf(buf)
// Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`.
const MAX_LEN: usize = 512;
let len = buf.len().min(MAX_LEN);
let result =
unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, buf.as_ptr() as u64, len as u64) };
if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) }
}
fn flush(&mut self) -> io::Result<()> {
@ -62,23 +51,7 @@ impl io::Write for Stdout {
}
}
impl Stderr {
pub const fn new() -> Stderr {
Stderr
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
print_buf(buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE;
pub fn is_ebadf(err: &io::Error) -> bool {
err.raw_os_error() == Some(libc::EBADF as i32)

View File

@ -11,13 +11,13 @@ use crate::os::hermit::io::FromRawFd;
use crate::os::unix::io::FromRawFd;
use crate::sys::fd::FileDesc;
pub struct Stdin(());
pub struct Stdout(());
pub struct Stderr(());
pub struct Stdin;
pub struct Stdout;
pub struct Stderr;
impl Stdin {
pub const fn new() -> Stdin {
Stdin(())
Stdin
}
}
@ -42,7 +42,7 @@ impl io::Read for Stdin {
impl Stdout {
pub const fn new() -> Stdout {
Stdout(())
Stdout
}
}
@ -68,7 +68,7 @@ impl io::Write for Stdout {
impl Stderr {
pub const fn new() -> Stderr {
Stderr(())
Stderr
}
}

View File

@ -1,8 +1,8 @@
use crate::io;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
pub struct Stdin;
pub struct Stdout;
pub struct Stderr;
pub type Stderr = Stdout;
impl Stdin {
pub const fn new() -> Stdin {
@ -11,9 +11,47 @@ impl Stdin {
}
impl io::Read for Stdin {
#[inline]
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
#[inline]
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)
}
}
impl Stdout {
@ -23,26 +61,35 @@ impl Stdout {
}
impl io::Write for Stdout {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
#[inline]
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let total_len = bufs.iter().map(|b| b.len()).sum();
Ok(total_len)
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
Ok(())
}
}
impl Stderr {
pub const fn new() -> Stderr {
Stderr
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
#[inline]
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
Ok(())
}
// Keep the default write_fmt so the `fmt::Arguments` are still evaluated.
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}

View File

@ -1,27 +1,18 @@
#[expect(dead_code)]
#[path = "unsupported.rs"]
mod unsupported_stdio;
use crate::io;
pub struct Stdin;
pub struct Stdout {}
pub struct Stderr;
use crate::os::xous::ffi::{Connection, lend, try_lend, try_scalar};
use crate::os::xous::services::{LogLend, LogScalar, log_server, try_connect};
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
}
pub type Stdin = unsupported_stdio::Stdin;
pub struct Stdout;
pub struct Stderr;
impl Stdout {
pub const fn new() -> Stdout {
Stdout {}
Stdout
}
}
@ -73,7 +64,7 @@ impl io::Write for Stderr {
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE;
pub fn is_ebadf(_err: &io::Error) -> bool {
true