Skip to content

Commit 7582f7e

Browse files
authored
Unrolled build for #146207
Rollup merge of #146207 - alexcrichton:wasip2-stdio, r=juntyr std: Implement WASIp2-specific stdio routines This commit is an extension of #145944 but applied to stdio specifically. The stdio routines are updated away from WASIp1 APIs to using WASIp2 APIs natively. The end goal is to eventually drop the dependency on WASIp1 APIs in the standard library entirely in favor of exclusively depending on WASIp2.
2 parents ad85bc5 + d8ca776 commit 7582f7e

File tree

3 files changed

+127
-3
lines changed

3 files changed

+127
-3
lines changed

library/std/src/sys/stdio/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ cfg_select! {
2929
mod uefi;
3030
pub use uefi::*;
3131
}
32-
target_os = "wasi" => {
33-
mod wasi;
34-
pub use wasi::*;
32+
all(target_os = "wasi", target_env = "p1") => {
33+
mod wasip1;
34+
pub use wasip1::*;
35+
}
36+
all(target_os = "wasi", target_env = "p2") => {
37+
mod wasip2;
38+
pub use wasip2::*;
3539
}
3640
target_os = "xous" => {
3741
mod xous;

library/std/src/sys/stdio/wasip2.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use wasip2::cli;
2+
use wasip2::io::streams::{Error, InputStream, OutputStream, StreamError};
3+
4+
use crate::io::{self, BorrowedBuf, BorrowedCursor};
5+
6+
pub struct Stdin(Option<InputStream>);
7+
pub struct Stdout(Option<OutputStream>);
8+
pub struct Stderr(Option<OutputStream>);
9+
10+
fn error_to_io(err: Error) -> io::Error {
11+
// There exists a function in `wasi:filesystem` to optionally acquire an
12+
// error code from an error, but the streams in use in this module are
13+
// exclusively used with stdio meaning that a filesystem error is not
14+
// possible here.
15+
//
16+
// In lieu of an error code, which WASIp2 does not specify, this instead
17+
// carries along the `to_debug_string` implementation that the host
18+
// supplies. If this becomes too expensive in the future this could also
19+
// become `io::Error::from_raw_os_error(libc::EIO)` or similar.
20+
io::Error::new(io::ErrorKind::Other, err.to_debug_string())
21+
}
22+
23+
impl Stdin {
24+
pub const fn new() -> Stdin {
25+
Stdin(None)
26+
}
27+
28+
fn stream(&mut self) -> &InputStream {
29+
self.0.get_or_insert_with(cli::stdin::get_stdin)
30+
}
31+
}
32+
33+
impl io::Read for Stdin {
34+
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
35+
let mut buf = BorrowedBuf::from(data);
36+
self.read_buf(buf.unfilled())?;
37+
Ok(buf.len())
38+
}
39+
40+
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
41+
match self.stream().blocking_read(u64::try_from(buf.capacity()).unwrap()) {
42+
Ok(result) => {
43+
buf.append(&result);
44+
Ok(())
45+
}
46+
Err(StreamError::Closed) => Ok(()),
47+
Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)),
48+
}
49+
}
50+
}
51+
52+
impl Stdout {
53+
pub const fn new() -> Stdout {
54+
Stdout(None)
55+
}
56+
57+
fn stream(&mut self) -> &OutputStream {
58+
self.0.get_or_insert_with(cli::stdout::get_stdout)
59+
}
60+
}
61+
62+
fn write(stream: &OutputStream, buf: &[u8]) -> io::Result<usize> {
63+
// WASIp2's `blocking_write_and_flush` function is defined as accepting no
64+
// more than 4096 bytes. Larger writes can be issued by manually using
65+
// `check_write`, `write`, and `blocking_flush`, but for now just go ahead
66+
// and use `blocking_write_and_flush` and report a short write and let a
67+
// higher level loop over the result.
68+
const MAX: usize = 4096;
69+
let buf = &buf[..buf.len().min(MAX)];
70+
match stream.blocking_write_and_flush(buf) {
71+
Ok(()) => Ok(buf.len()),
72+
Err(StreamError::Closed) => Ok(0),
73+
Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)),
74+
}
75+
}
76+
77+
impl io::Write for Stdout {
78+
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
79+
write(self.stream(), data)
80+
}
81+
82+
fn flush(&mut self) -> io::Result<()> {
83+
// Note that `OutputStream` has a `flush` function but for stdio all
84+
// writes are accompanied with a flush which means that this flush
85+
// doesn't need to do anything.
86+
Ok(())
87+
}
88+
}
89+
90+
impl Stderr {
91+
pub const fn new() -> Stderr {
92+
Stderr(None)
93+
}
94+
95+
fn stream(&mut self) -> &OutputStream {
96+
self.0.get_or_insert_with(cli::stderr::get_stderr)
97+
}
98+
}
99+
100+
impl io::Write for Stderr {
101+
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
102+
write(self.stream(), data)
103+
}
104+
105+
fn flush(&mut self) -> io::Result<()> {
106+
// See `Stdout::flush` for why this is a noop.
107+
Ok(())
108+
}
109+
}
110+
111+
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
112+
113+
pub fn is_ebadf(_err: &io::Error) -> bool {
114+
// WASIp2 stdio streams are always available so ebadf never shows up.
115+
false
116+
}
117+
118+
pub fn panic_output() -> Option<impl io::Write> {
119+
Some(Stderr::new())
120+
}

0 commit comments

Comments
 (0)