use crate::{Error, ErrorKind, Result};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
use std::{
alloc::{Layout, alloc, handle_alloc_error, realloc},
cell::Cell,
ptr,
};
use wasip2::io::streams::{InputStream, StreamError};
use wasip2::sockets::udp::{IncomingDatagramStream, OutgoingDatagramStream};
#[derive(Clone, Copy)]
struct Arena {
ptr: *mut u8,
len: usize,
used: usize,
align: usize,
}
thread_local! { static ARENA: Cell<Option<Arena>> = const { Cell::new(None) }; }
struct Reset;
impl Drop for Reset {
fn drop(&mut self) {
ARENA.set(None);
}
}
fn scoped<T>(storage: &mut [u32], f: impl FnOnce() -> T) -> T {
ARENA.with(|a| {
assert!(a.get().is_none(), "nested canonical return arena");
a.set(Some(Arena {
ptr: storage.as_mut_ptr().cast(),
len: storage.len() * 4,
used: 0,
align: 4,
}));
});
let _reset = Reset;
f()
}
fn scoped_bytes<T>(storage: &mut [u8], f: impl FnOnce() -> T) -> T {
ARENA.with(|a| {
assert!(a.get().is_none(), "nested canonical return arena");
a.set(Some(Arena {
ptr: storage.as_mut_ptr(),
len: storage.len(),
used: 0,
align: 1,
}));
});
let _reset = Reset;
f()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn cabi_realloc(
old: *mut u8,
old_len: usize,
align: usize,
len: usize,
) -> *mut u8 {
assert!(align.is_power_of_two());
if len == 0 {
return ptr::without_provenance_mut(align);
}
if let Some(mut arena) = ARENA.get() {
assert_eq!(old_len, 0);
assert!(align <= arena.align);
let offset = (arena.used + align - 1) & !(align - 1);
assert!(
len <= arena.len.saturating_sub(offset),
"canonical result exceeds reserved capacity"
);
arena.used = offset + len;
ARENA.set(Some(arena));
return unsafe { arena.ptr.add(offset) };
}
let layout = Layout::from_size_align(if old_len == 0 { len } else { old_len }, align)
.expect("canonical layout");
let result = unsafe {
if old_len == 0 {
alloc(layout)
} else {
realloc(old, layout, len)
}
};
if result.is_null() {
handle_alloc_error(layout);
}
result
}
#[link(wasm_import_module = "wasi:io/poll@0.2.9")]
unsafe extern "C" {
#[link_name = "poll"]
fn raw_poll(input: *const u32, len: usize, result: *mut usize);
}
#[link(wasm_import_module = "wasi:io/streams@0.2.9")]
unsafe extern "C" {
#[link_name = "[method]input-stream.read"]
fn raw_read(handle: u32, len: u64, result: *mut u32);
}
#[link(wasm_import_module = "wasi:sockets/udp@0.2.9")]
unsafe extern "C" {
#[link_name = "[method]incoming-datagram-stream.receive"]
fn raw_receive(handle: u32, len: u64, result: *mut u32);
#[link_name = "[method]outgoing-datagram-stream.send"]
fn raw_send(handle: u32, input: *const u32, len: usize, result: *mut u64);
}
#[link(wasm_import_module = "wasi:filesystem/types@0.2.9")]
unsafe extern "C" {
#[link_name = "[method]descriptor.read"]
fn raw_file_read(handle: i32, length: i64, offset: i64, result: *mut u8);
}
pub fn file_read(
descriptor: &wasip2::filesystem::types::Descriptor,
output: &mut [u8],
offset: u64,
) -> std::result::Result<(usize, bool), u8> {
let mut result = [0u32; 4];
let base = output.as_mut_ptr();
let capacity = output.len();
scoped_bytes(output, || {
unsafe {
raw_file_read(
descriptor.handle() as i32,
capacity as i64,
offset as i64,
result.as_mut_ptr().cast(),
);
}
match result[0] & 0xff {
0 => {
let (pointer, n) = (result[1] as usize as *mut u8, result[2] as usize);
assert!(n <= capacity);
if n != 0 && pointer != base {
unsafe { ptr::copy(pointer, base, n) };
}
Ok((n, result[3] & 0xff != 0))
}
1 => Err((result[1] & 0xff) as u8),
_ => unreachable!("invalid read result"),
}
})
}
pub fn poll(input: &[u32], storage: &mut [u32], ready: &mut Vec<usize>) {
assert!(!input.is_empty());
let mut result = [0usize; 2];
scoped(storage, || {
unsafe {
raw_poll(input.as_ptr(), input.len(), result.as_mut_ptr());
}
assert!(result[1] <= input.len());
if result[1] == 0 {
return;
}
let indices = unsafe { std::slice::from_raw_parts(result[0] as *const u32, result[1]) };
for &i in indices {
assert!((i as usize) < input.len());
ready.push(i as usize);
}
});
}
pub fn read(
stream: &InputStream,
output: &mut [u8],
storage: &mut [u32],
) -> std::result::Result<usize, StreamError> {
let mut result = [0u32; 3];
scoped(storage, || {
unsafe {
raw_read(stream.handle(), output.len() as u64, result.as_mut_ptr());
}
match result[0] & 0xff {
0 => {
let n = result[2] as usize;
assert!(n <= output.len());
if n == 0 {
return Ok(0);
}
unsafe {
ptr::copy_nonoverlapping(result[1] as *const u8, output.as_mut_ptr(), n);
}
Ok(n)
}
1 if result[1] & 0xff == 1 => Err(StreamError::Closed),
1 => {
Err(StreamError::LastOperationFailed(unsafe {
wasip2::io::error::Error::from_handle(result[2])
}))
}
_ => unreachable!("invalid stream result"),
}
})
}
fn decode_addr(bytes: &[u8]) -> SocketAddr {
let port = u16::from_le_bytes([bytes[4], bytes[5]]);
if bytes[0] == 0 {
SocketAddr::from((Ipv4Addr::new(bytes[6], bytes[7], bytes[8], bytes[9]), port))
} else {
let segments =
std::array::from_fn(|i| u16::from_le_bytes([bytes[12 + i * 2], bytes[13 + i * 2]]));
let flow = u32::from_le_bytes(bytes[8..12].try_into().expect("flow"));
let scope = u32::from_le_bytes(bytes[28..32].try_into().expect("scope"));
SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::from(segments),
port,
flow,
scope,
))
}
}
pub fn receive(
stream: &IncomingDatagramStream,
output: &mut [u8],
storage: &mut [u32],
) -> Result<Option<(usize, SocketAddr)>> {
let mut result = [0u32; 3];
scoped(storage, || {
unsafe {
raw_receive(stream.handle(), 1, result.as_mut_ptr());
}
if result[0] & 0xff != 0 {
return Err(Error::new(ErrorKind::Other));
}
if result[2] == 0 {
return Ok(None);
}
assert_eq!(result[2], 1);
let record = unsafe { std::slice::from_raw_parts(result[1] as *const u32, 10) };
let n = (record[1] as usize).min(output.len());
if n != 0 {
unsafe {
ptr::copy_nonoverlapping(record[0] as *const u8, output.as_mut_ptr(), n);
}
}
let address = unsafe { std::slice::from_raw_parts(record.as_ptr().add(2).cast(), 32) };
Ok(Some((n, decode_addr(address))))
})
}
pub fn send(stream: &OutgoingDatagramStream, bytes: &[u8], to: SocketAddr) -> Result<usize> {
if bytes.len() > 65535 {
return Err(Error::new(ErrorKind::InvalidInput));
}
let mut record = [0u32; 11];
record[0] = bytes.as_ptr() as u32;
record[1] = bytes.len() as u32;
record[2] = 1;
let address =
unsafe { std::slice::from_raw_parts_mut(record.as_mut_ptr().add(3).cast::<u8>(), 32) };
address[4..6].copy_from_slice(&to.port().to_le_bytes());
match to {
SocketAddr::V4(a) => address[6..10].copy_from_slice(&a.ip().octets()),
SocketAddr::V6(a) => {
address[0] = 1;
address[8..12].copy_from_slice(&a.flowinfo().to_le_bytes());
for (i, s) in a.ip().segments().into_iter().enumerate() {
address[12 + i * 2..14 + i * 2].copy_from_slice(&s.to_le_bytes());
}
address[28..32].copy_from_slice(&a.scope_id().to_le_bytes());
}
}
let mut result = [0u64; 2];
unsafe {
raw_send(stream.handle(), record.as_ptr(), 1, result.as_mut_ptr());
}
if result[0] & 0xff != 0 {
return Err(Error::new(ErrorKind::Other));
}
if result[1] == 0 {
return Err(Error::new(ErrorKind::WouldBlock));
}
assert_eq!(result[1], 1);
Ok(bytes.len())
}