Skip to main content

warp_runtime/
output.rs

1//! The one WASI call warp programs make, `fd_write` (print), for a store of any state: the warp-runtime stub needs no WASI library
2use wasmtime::{Caller, Extern, Linker, Result};
3
4pub const WASI_LIBRARY: &str = "wasi_snapshot_preview1";
5pub const FD_WRITE: &str = "fd_write";
6const STDOUT: i32 = 1;
7const STDERR: i32 = 2;
8const ERRNO_SUCCESS: i32 = 0;
9const ERRNO_BADF: i32 = 8;
10const ERRNO_FAULT: i32 = 21;
11const IOVEC_BYTES: usize = 8;
12
13/// fd_write(fd, iovecs, count, written) to stdout or stderr
14pub fn link_output<T: 'static>(linker: &mut Linker<T>) -> Result<()> {
15	linker.func_wrap(
16		WASI_LIBRARY,
17		FD_WRITE,
18		|mut caller: Caller<'_, T>, fd: i32, iovecs: i32, count: i32, written: i32| -> i32 {
19			let Some(Extern::Memory(memory)) = caller.get_export("memory") else {
20				return ERRNO_FAULT;
21			};
22			let bytes = memory.data(&caller);
23			let word =
24				|at: usize| bytes.get(at..at + 4).map(|word| u32::from_le_bytes(word.try_into().unwrap()) as usize);
25			let mut text = Vec::new();
26			for index in 0..count.max(0) as usize {
27				let at = iovecs as usize + index * IOVEC_BYTES;
28				let (Some(start), Some(length)) = (word(at), word(at + 4)) else {
29					return ERRNO_FAULT;
30				};
31				let Some(part) = bytes.get(start..start + length) else {
32					return ERRNO_FAULT;
33				};
34				text.extend_from_slice(part);
35			}
36			use std::io::Write;
37			let outcome = match fd {
38				STDOUT => std::io::stdout().write_all(&text),
39				STDERR => std::io::stderr().write_all(&text),
40				_ => return ERRNO_BADF,
41			};
42			if outcome.is_err() {
43				return ERRNO_BADF;
44			}
45			match memory.write(&mut caller, written as usize, &(text.len() as u32).to_le_bytes()) {
46				Ok(()) => ERRNO_SUCCESS,
47				Err(_) => ERRNO_FAULT,
48			}
49		},
50	)?;
51	Ok(())
52}