1use 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
13pub 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}