///|
fn args_sizes_get_ffi(argc : Int, size : Int) -> UInt = "wasi_snapshot_preview1" "args_sizes_get"
///|
/// Return command-line argument data sizes.
///
/// @return Returns the number of arguments and the size of the argument string data, or an error.
pub fn args_sizes_get() -> (Int, Int) raise Errno {
let argc_ptr = @ffi.malloc(4)
defer @ffi.free(argc_ptr)
let size_ptr = @ffi.malloc(4)
defer @ffi.free(size_ptr)
let ret = args_sizes_get_ffi(argc_ptr, size_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let argc = @ffi.load32(argc_ptr)
let size = @ffi.load32(size_ptr)
(argc, size)
}
///|
fn args_get_ffi(argv : Int, argv_buf : Int) -> UInt = "wasi_snapshot_preview1" "args_get"
///|
/// Read command-line argument data.
///
/// The size of the array should match that returned by `args_sizes_get`.
///
/// Each argument is **not** `\0` terminated.
///
/// The first argument should be a string containing the "name" of the
/// program. This need not be a usable filesystem path or even file name,
/// and may even be a fixed string. Subsequent arguments are the arguments
/// passed to the program by the user.
pub fn args_get() -> Array[String] raise Errno {
let (argc, size) = args_sizes_get()
if argc == 0 {
return []
}
let arg_ptr_ptr = @ffi.malloc(argc * 4)
defer @ffi.free(arg_ptr_ptr)
let arg_buf = Bytes::make(size, b'\x00')
let arg_buf_ptr = @ffi.bytes2ptr(arg_buf)
defer @ffi.free(arg_buf_ptr)
let ret = args_get_ffi(arg_ptr_ptr, arg_buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let args = []
for i in 0.. @encoding/utf8.decode_lossy(),
)
}
args
}
///|
fn environ_sizes_get_ffi(argc : Int, size : Int) -> UInt = "wasi_snapshot_preview1" "environ_sizes_get"
///|
/// Return environment variable data sizes.
///
/// @return Returns the number of environment variables arguments and the size of the environment variable data.
pub fn environ_sizes_get() -> (Int, Int) raise Errno {
let argc_ptr = @ffi.malloc(4)
defer @ffi.free(argc_ptr)
let size_ptr = @ffi.malloc(4)
defer @ffi.free(size_ptr)
let ret = environ_sizes_get_ffi(argc_ptr, size_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let argc = @ffi.load32(argc_ptr)
let size = @ffi.load32(size_ptr)
(argc, size)
}
///|
fn environ_get_ffi(environ : Int, environ_buf : Int) -> UInt = "wasi_snapshot_preview1" "environ_get"
///|
/// Read environment variable data.
///
/// The size of the array should match that returned by `environ_sizes_get`.
pub fn environ_get() -> Array[(String, String)] raise Errno {
let (argc, size) = environ_sizes_get()
if argc == 0 {
return []
}
let environ_ptr_ptr = @ffi.malloc(argc * 4)
defer @ffi.free(environ_ptr_ptr)
let environ_buf = Bytes::make(size, b'\x00')
let environ_buf_ptr = @ffi.bytes2ptr(environ_buf)
defer @ffi.free(environ_buf_ptr)
let ret = environ_get_ffi(environ_ptr_ptr, environ_buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let envs = []
for i in 0.. @encoding/utf8.decode_lossy()
guard str.split("=").collect() is [key, .. value]
envs.push(
(key.to_string(), value.iter().map(StringView::to_string).join("=")),
)
}
envs
}
///|
fn clock_res_get_ffi(clock_id : UInt, resolution : Int) -> UInt = "wasi_snapshot_preview1" "clock_res_get"
///|
/// Return the resolution of a clock.
///
/// Implementations are required to provide a non-zero value for supported clocks.
/// For unsupported clocks, return `errno::inval`.
///
/// Note: This is similar to `clock_getres` in POSIX.
///
/// @param `self` The clock for which to return the resolution.
/// @return The resolution of the clock, or an error if one happened.
pub fn ClockId::clock_res_get(self : ClockId) -> TimeStamp raise Errno {
let resolution_ptr = @ffi.malloc(8)
defer @ffi.free(resolution_ptr)
let ret = clock_res_get_ffi(self.value(), resolution_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let resolution = @ffi.load64_u(resolution_ptr)
resolution
}
///|
fn clock_time_get_ffi(clock_id : UInt, precision : UInt64, time : Int) -> UInt = "wasi_snapshot_preview1" "clock_time_get"
///|
/// Return the time value of a clock
///
/// Note: This is similar to `clock_gettime` in POSIX.
///
/// @param `self` The clodk for which to return the time.
/// @param `precision` The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
/// @return The time value of the clock.
pub fn ClockId::clock_time_get(
self : ClockId,
precision : TimeStamp,
) -> TimeStamp raise Errno {
let time_ptr = @ffi.malloc(8)
defer @ffi.free(time_ptr)
let ret = clock_time_get_ffi(self.value(), precision.0, time_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let time = @ffi.load64_u(time_ptr)
time
}
///|
fn fd_advise_ffi(
fd : Int,
offset : UInt64,
len : UInt64,
advice : UInt,
) -> UInt = "wasi_snapshot_preview1" "fd_advise"
///|
/// Provide file advisory information on a file ddescriptor
///
/// Note: This is similar to `posix_fadvise` in POSIX.
///
/// @param `offset` The offset within the file to which the advisory applies.
/// @param `len` The length of the region to which the advisory applies.
/// @param `advice` The advice.
pub fn Fd::fd_advise(
self : Fd,
offset : FileSize,
len : FileSize,
advice : Advice,
) -> Unit raise Errno {
let ret = fd_advise_ffi(self.0, offset.0, len.0, advice.value())
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_allocate_ffi(fd : Int, offset : UInt64, len : UInt64) -> UInt = "wasi_snapshot_preview1" "fd_allocate"
///|
/// Force the allocaiton of space in a file.
///
/// Note: This is similar to `posix_fallocate` in POSIX.
///
/// @param `offset` The offset at which to start allocating.
/// @param `len` The length of the area that is allocated.
/// @return The error code.
pub fn Fd::fd_allocate(
self : Fd,
offset : FileSize,
len : FileSize,
) -> Unit raise Errno {
let ret = fd_allocate_ffi(self.0, offset.0, len.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_close_ffi(fd : Int) -> UInt = "wasi_snapshot_preview1" "fd_close"
///|
/// Close a file descriptor.
///
/// Note: This is similar to `close` in POSIX.
pub fn Fd::fd_close(self : Fd) -> Unit raise Errno {
let ret = fd_close_ffi(self.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_datasync_ffi(fd : Int) -> UInt = "wasi_snapshot_preview1" "fd_datasync"
///|
/// Synchronize the data of a file to disk.
///
/// Note: This is similar to `fdatasync` in POSIX.
pub fn Fd::fd_datasync(self : Fd) -> Unit raise Errno {
let ret = fd_datasync_ffi(self.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_fdstat_get_ffi(fd : Int, buf : Int) -> UInt = "wasi_snapshot_preview1" "fd_fdstat_get"
///|
/// Get the attributes of a file descriptor.
///
/// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX, as well as additional fields.
pub fn Fd::fd_fdstat_get(self : Fd) -> FdStat raise Errno {
let buf_ptr = @ffi.malloc(24)
defer @ffi.free(buf_ptr)
let ret = fd_fdstat_get_ffi(self.0, buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let fs_filetype : FileType = @ffi.load8_u(buf_ptr)
|> FileType::from_value
|> Option::unwrap
let fs_flags : FdFlags = @ffi.load16_u(buf_ptr + 2)
let fs_rights_base : Rights = @ffi.load64_u(buf_ptr + 8)
let fs_rights_inheriting : Rights = @ffi.load64_u(buf_ptr + 16)
let buf = FdStat::{
fs_filetype,
fs_flags,
fs_rights_base,
fs_rights_inheriting,
}
buf
}
///|
fn fd_fdstat_set_flags_ffi(fd : Int, flags : UInt) -> UInt = "wasi_snapshot_preview1" "fd_fdstat_set_flags"
///|
/// Adjust the flags associated with a file descriptor.
///
/// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
///
/// @param `flags` The desired values of the file descriptor flags.
pub fn Fd::fd_fdstat_set_flags(self : Fd, flags : FdFlags) -> Unit raise Errno {
let ret = fd_fdstat_set_flags_ffi(self.0, flags.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_fdstat_set_rights_ffi(
fd : Int,
fs_rights_base : UInt64,
fs_rights_inheriting : UInt64,
) -> UInt = "wasi_snapshot_preview1" "fd_fdstat_set_rights"
///|
/// Adjust the rights associated with a file descriptor.
///
/// This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights.
///
/// @param `fs_rights_base` The desired rights of the file descriptor.
pub fn Fd::fd_fdstat_set_rights(
self : Fd,
fs_rights_base : Rights,
fs_rights_inheriting : Rights,
) -> Unit raise Errno {
let ret = fd_fdstat_set_rights_ffi(
self.0,
fs_rights_base.0,
fs_rights_inheriting.0,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_filestat_get_ffi(fd : Int, buf : Int) -> UInt = "wasi_snapshot_preview1" "fd_filestat_get"
///|
/// Return the attributes of an open file
pub fn Fd::fd_filestat_get(self : Fd) -> FileStat raise Errno {
let buf_ptr = @ffi.malloc(64)
defer @ffi.free(buf_ptr)
let ret = fd_filestat_get_ffi(self.0, buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let dev : Device = @ffi.load64_u(buf_ptr)
let ino : Inode = @ffi.load64_u(buf_ptr + 8)
let filetype : FileType = @ffi.load8_u(buf_ptr + 16)
|> FileType::from_value
|> Option::unwrap
let nlink : LinkCount = @ffi.load64_u(buf_ptr + 24)
let size : FileSize = @ffi.load64_u(buf_ptr + 32)
let atim : TimeStamp = @ffi.load64_u(buf_ptr + 40)
let mtim : TimeStamp = @ffi.load64_u(buf_ptr + 48)
let ctim : TimeStamp = @ffi.load64_u(buf_ptr + 56)
let buf = FileStat::{ dev, ino, filetype, nlink, size, atim, mtim, ctim }
buf
}
///|
fn fd_filestat_set_size_ffi(ffi : Int, size : UInt64) -> UInt = "wasi_snapshot_preview1" "fd_filestat_set_size"
///|
/// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
///
/// Note: This is similar to `ftruncate` in POSIX.
///
/// @param `size` The desired file size.
pub fn Fd::fd_filestat_set_size(self : Fd, size : FileSize) -> Unit raise Errno {
let ret = fd_filestat_set_size_ffi(self.0, size.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_filestat_set_times_ffi(
fd : Int,
atim : UInt64,
mtim : UInt64,
fst_flags : UInt,
) -> UInt = "wasi_snapshot_preview1" "fd_filestat_set_times"
///|
/// Adjust the timestamps of an open file or directory.
///
/// Note: This is similar to `futimens` in POSIX.
///
/// @param `atim` The desired value of the data access timestamp.
/// @param `mtim` The desired value of the data modification timestamp.
/// @param `fst_flags` A bitmask indicating which timestamps to adjust.
pub fn Fd::fd_filestat_set_times(
self : Fd,
atim : TimeStamp,
mtim : TimeStamp,
fst_flags : FstFlags,
) -> Unit raise Errno {
let ret = fd_filestat_set_times_ffi(self.0, atim.0, mtim.0, fst_flags.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_pread_ffi(
fd : Int,
iovs : Int,
iovs_len : Int,
offset : UInt64,
nread : Int,
) -> UInt = "wasi_snapshot_preview1" "fd_pread"
///|
/// Read from a file descriptor, without using and updating the file descriptor's offset.
///
/// Note: This is similar to `preadv` in Linux (and other Unix-es).
pub fn Fd::fd_pread(
self : Fd,
iovs : Array[FixedArray[Byte]],
offset : FileSize,
) -> Size raise Errno {
let iovs_len = iovs.length()
let iovs_ptr = @ffi.malloc(iovs_len * 8)
defer @ffi.free(iovs_ptr)
for i, byte in iovs {
let ptr = @ffi.byte_array2ptr(byte)
@ffi.store32(iovs_ptr + i * 8, ptr)
@ffi.store32(iovs_ptr + i * 8 + 4, byte.length())
}
defer (for i in 0.. UInt = "wasi_snapshot_preview1" "fd_prestat_get"
///|
/// Return a description of the given preopened file descriptor.
///
/// Note: consider using `fd_prestat_dir_name` directly.
pub fn Fd::fd_prestat_get(self : Fd) -> PreStat raise Errno {
let buf_ptr = @ffi.malloc(8)
defer @ffi.free(buf_ptr)
let ret = fd_prestat_get_ffi(self.0, buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
// There's no other possibility of tag...
guard @ffi.load8_u(buf_ptr) is 0
let size : Size = @ffi.load32_u(buf_ptr + 4)
let buf = PreStat::Dir(PreStat_dir::{ pr_name_len: size })
buf
}
///|
fn fd_prestat_dir_name_ffi(fd : Int, path : Int, path_len : Int) -> UInt = "wasi_snapshot_preview1" "fd_prestat_dir_name"
///|
/// Return a description of the given preopened file descriptor.
pub fn Fd::fd_prestat_dir_name(self : Fd) -> String raise Errno {
let prestat = self.fd_prestat_get()
guard prestat is Dir({ pr_name_len: Size(size) })
let buffer = Bytes::make(size.reinterpret_as_int(), b'\x00')
let buf_ptr = @ffi.bytes2ptr(buffer)
defer @ffi.free(buf_ptr)
let ret = fd_prestat_dir_name_ffi(self.0, buf_ptr, size.reinterpret_as_int())
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
if buffer is [.. rest, b'\x00'] {
@encoding/utf8.decode_lossy(rest)
} else {
@encoding/utf8.decode_lossy(buffer)
}
}
///|
fn fd_read_ffi(fd : Int, iovs : Int, iovs_len : Int, nread : Int) -> UInt = "wasi_snapshot_preview1" "fd_read"
///|
/// Read from a file descriptor.
///
/// Note: This is similar to `readv` in POSIX.
pub fn Fd::fd_read(
self : Fd,
iovs : Array[FixedArray[Byte]],
) -> Size raise Errno {
let iovs_len = iovs.length()
let iovs_ptr = @ffi.malloc(iovs_len * 8)
defer @ffi.free(iovs_ptr)
for i, byte in iovs {
let ptr = @ffi.byte_array2ptr(byte)
@ffi.store32(iovs_ptr + i * 8, ptr)
@ffi.store32(iovs_ptr + i * 8 + 4, byte.length())
}
defer (for i in 0.. UInt = "wasi_snapshot_preview1" "fd_readdir"
///|
/// Read directory entries from a directory.
/// When successful, the contents of the output buffer consist of a sequence of
/// directory entries. Each directory entry consists of a `dirent` object,
/// followed by `dirent::d_namlen` bytes holding the name of the directory
/// entry.
///
/// This function fills the output buffer as much as possible, potentially
/// truncating the last directory entry. This allows the caller to grow its
/// read buffer size in case it's too small to fit a single large directory
/// entry, or skip the oversized directory entry.
///
/// Entries for the special `.` and `..` directory entries are included in the
/// sequence.
///
/// @param `cookie`: The location within the directory to start reading.
/// @param `size`: The buffer used to read.
/// @return The entries untruncated and whether there's still other entries.
pub fn Fd::fd_readdir(
self : Fd,
cookie? : DirCookie = DirCookie(0),
buffer? : Bytes = Bytes::make(4096, 0),
) -> (Array[(String, DirEnt)], Bool) raise Errno {
let buf_len = buffer.length()
let buf_ptr = @ffi.bytes2ptr(buffer)
defer @ffi.free(buf_ptr)
let buf_used_ptr = @ffi.malloc(4)
defer @ffi.free(buf_used_ptr)
let ret = fd_readdir_ffi(self.0, buf_ptr, buf_len, cookie.0, buf_used_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let buf_used = @ffi.load32(buf_used_ptr)
let entries = []
for i = 0; i < buf_used; {
if i + 24 > buf_used {
// truncated
break (entries, true)
}
let d_next : DirCookie = @ffi.load64_u(buf_ptr + i)
let d_ino : Inode = @ffi.load64_u(buf_ptr + i + 8)
let namlen = @ffi.load32(buf_ptr + i + 16)
let d_namlen : DirNamlen = namlen.reinterpret_as_uint()
let d_type : FileType = @ffi.load8_u(buf_ptr + i + 20)
|> FileType::from_value
|> Option::unwrap
if i + 24 + namlen > buf_used {
// truncated
break (entries, true)
}
entries.push(
(
buffer[i + 24:i + 24 + namlen] |> @encoding/utf8.decode_lossy(),
DirEnt::{ d_next, d_ino, d_namlen, d_type },
),
)
continue i + 24 + namlen
} nobreak {
(entries, buf_used == buf_len)
}
}
///|
fn fd_renumber_ffi(fd : Int, to : Int) -> UInt = "wasi_snapshot_preview1" "fd_renumber"
///|
/// Atomically replace a file descriptor by renumbering another file descriptor.
///
/// Due to the strong focus on thread safety, this environment does not provide
/// a mechanism to duplicate or renumber a file descriptor to an arbitrary
/// number, like `dup2()`. This would be prone to race conditions, as an actual
/// file descriptor with the same number could be allocated by a different
/// thread at the same time.
///
/// This function provides a way to atomically renumber file descriptors, which
/// would disappear if `dup2()` were to be removed entirely.
pub fn Fd::fd_renumber(self : Fd, to : Fd) -> Unit raise Errno {
let ret = fd_renumber_ffi(self.0, to.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_seek_ffi(
fd : Int,
offset : Int64,
whence : UInt,
newoffset : Int,
) -> UInt = "wasi_snapshot_preview1" "fd_seek"
///|
/// Move the offset of a file descriptor.
///
/// Note: This is similar to `lseek` in POSIX.
///
/// @param `offset` The number of bytes to move.
/// @param `whence` The base from which the offset is calculated.
/// @return The new offset of the file descriptor, relative to the start of the file.
pub fn Fd::fd_seek(
self : Fd,
offset : FileDelta,
whence : Whence,
) -> FileSize raise Errno {
let newoffset_ptr = @ffi.malloc(8)
defer @ffi.free(newoffset_ptr)
let ret = fd_seek_ffi(self.0, offset.0, whence.value(), newoffset_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let newoffset = @ffi.load64_u(newoffset_ptr)
newoffset
}
///|
fn fd_sync_ffi(fd : Int) -> UInt = "wasi_snapshot_preview1" "fd_sync"
///|
/// Synchronize the data and metadata of a file to disk.
///
/// Note: This is similar to `fsync` in POSIX.
pub fn Fd::fd_sync(self : Fd) -> Unit raise Errno {
let ret = fd_sync_ffi(self.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn fd_tell_ffi(fd : Int, newoffset : Int) -> UInt = "wasi_snapshot_preview1" "fd_tell"
///|
/// Return the current offset of a file descriptor.
///
/// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
///
/// @return The current offset of the file descriptor, relative to the start of the file.
pub fn Fd::fd_tell(self : Fd) -> FileSize raise Errno {
let newoffset_ptr = @ffi.malloc(8)
defer @ffi.free(newoffset_ptr)
let ret = fd_tell_ffi(self.0, newoffset_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let newoffset = @ffi.load64_u(newoffset_ptr)
newoffset
}
///|
fn fd_write_ffi(fd : Int, iovs : Int, iovs_len : Int, nwritten : Int) -> UInt = "wasi_snapshot_preview1" "fd_write"
///|
/// Write to a file descriptor.
///
/// Note: This is similar to `writev` in POSIX.
///
/// Like POSIX, any calls of `write` (and other functions to read or write)
/// for a regular file by other threads in the WASI process should not be
/// interleaved while `write` is executed.
///
/// Note: it may write less than demanded.
pub fn Fd::fd_write(self : Fd, contents : Array[BytesView]) -> Size raise Errno {
let iovs_len = contents.length()
let iovs_ptr = @ffi.malloc(iovs_len * 8)
defer @ffi.free(iovs_ptr)
for i, byte in contents {
let ptr = @ffi.bytes2ptr(byte.data())
@ffi.store32(iovs_ptr + i * 8, ptr + byte.start_offset())
@ffi.store32(iovs_ptr + i * 8 + 4, byte.length())
}
defer (for i in contents {
let ptr = @ffi.bytes2ptr(i.data())
@ffi.free(ptr)
@ffi.free(ptr)
})
let nwritten_ptr = @ffi.malloc(4)
defer @ffi.free(nwritten_ptr)
let ret = fd_write_ffi(self.0, iovs_ptr, iovs_len, nwritten_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let nwritten = @ffi.load32_u(nwritten_ptr)
nwritten
}
///|
fn path_create_directory_ffi(fd : Int, path : Int, path_len : Int) -> UInt = "wasi_snapshot_preview1" "path_create_directory"
///|
/// Create a directory.
///
/// Note: This is similar to `mkdirat` in POSIX.
///
/// @param `path` The path at which to create the directory.
pub fn Fd::path_create_directory(self : Fd, path : String) -> Unit raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let ret = path_create_directory_ffi(self.0, path_ptr, path_len)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_filestat_get_ffi(
fd : Int,
flags : UInt,
path : Int,
path_len : Int,
buf : Int,
) -> UInt = "wasi_snapshot_preview1" "path_filestat_get"
///|
/// Return the attributes of a file or directory.
///
/// Note: This is similar to `stat` in POSIX.
///
/// @param `flags` Flags determining the method of how the path is resolved.
/// @param `path` The path of the file or directory to inspect.
pub fn Fd::path_filestat_get(
self : Fd,
flags : LookupFlags,
path : String,
) -> FileStat raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let buf_ptr = @ffi.malloc(64)
defer @ffi.free(buf_ptr)
let ret = path_filestat_get_ffi(self.0, flags.0, path_ptr, path_len, buf_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let dev : Device = @ffi.load64_u(buf_ptr)
let ino : Inode = @ffi.load64_u(buf_ptr + 8)
let filetype : FileType = @ffi.load8_u(buf_ptr + 16)
|> FileType::from_value
|> Option::unwrap
let nlink : LinkCount = @ffi.load64_u(buf_ptr + 24)
let size : FileSize = @ffi.load64_u(buf_ptr + 32)
let atim : TimeStamp = @ffi.load64_u(buf_ptr + 40)
let mtim : TimeStamp = @ffi.load64_u(buf_ptr + 48)
let ctim : TimeStamp = @ffi.load64_u(buf_ptr + 56)
let buf = FileStat::{ dev, ino, filetype, nlink, size, atim, mtim, ctim }
buf
}
///|
fn path_filestat_set_times_ffi(
fd : Int,
flags : UInt,
path : Int,
path_len : Int,
atim : UInt64,
mtim : UInt64,
fst_flags : UInt,
) -> UInt = "wasi_snapshot_preview1" "path_filestat_set_times"
///|
/// Adjust the timestamps of a file or directory.
///
/// Note: This is similar to `utimensat` in POSIX.
///
/// @param `flag` Flags determining the method of how the path is resolved.
/// @param `path` The path of the file or directory to operate on.
/// @param `atim` The desired values of the data access timestamp.
/// @param `mtim` The desired values of the data modification timestamp.
/// @param `fst_flags` A bitmask indicating which timestamps to adjust.
pub fn Fd::path_filestat_set_times(
self : Fd,
flags : LookupFlags,
path : String,
atim : TimeStamp,
mtim : TimeStamp,
fst_flags : FstFlags,
) -> Unit raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let ret = path_filestat_set_times_ffi(
self.0,
flags.0,
path_ptr,
path_len,
atim.0,
mtim.0,
fst_flags.0,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_link_ffi(
old_fd : Int,
old_flags : UInt,
old_path : Int,
old_path_len : Int,
new_fd : Int,
new_path : Int,
new_path_len : Int,
) -> UInt = "wasi_snapshot_preview1" "path_link"
///|
/// Create a hard link.
///
/// Note: This is similar to `linkat` in POSIX.
///
/// @param `old_flags` Flags determining the method of how the path is resolved.
/// @param `old_path` The source path from which to link.
/// @param `new_fd` The wroking directory at which the resolution of the new path starts.
/// @param `new_path` The destination path at which to create the hard link.
pub fn Fd::path_link(
self : Fd,
old_flags : LookupFlags,
old_path : String,
new_fd : Fd,
new_path : String,
) -> Unit raise Errno {
let old_path_bytes = old_path |> @encoding/utf8.encode()
let old_path_len = old_path_bytes.length()
let old_path_ptr = @ffi.bytes2ptr(old_path_bytes)
defer @ffi.free(old_path_ptr)
let new_path_bytes = new_path |> @encoding/utf8.encode()
let new_path_len = new_path_bytes.length()
let new_path_ptr = @ffi.bytes2ptr(new_path_bytes)
defer @ffi.free(new_path_ptr)
let ret = path_link_ffi(
self.0,
old_flags.0,
old_path_ptr,
old_path_len,
new_fd.0,
new_path_ptr,
new_path_len,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_open_ffi(
dirfd : Int,
dirflags : UInt,
path : Int,
path_len : Int,
oflags : UInt,
fs_rights_base : UInt64,
fs_rights_inheriting : UInt64,
fs_flags : UInt,
fd : Int,
) -> UInt = "wasi_snapshot_preview1" "path_open"
///|
/// Open a file or directory.
///
/// The returned file descriptor is not guaranteed to be the lowest-numbered
/// file descriptor not currently open; it is randomized to prevent
/// applications from depending on making assumptions about indexes, since this
/// is error-prone in multi-threaded contexts. The returned file descriptor is
/// guaranteed to be less than 2^31.
///
/// Note: This is similar to `openat` in POSIX.
///
/// @param `dirflags` Flags determining the method of how the path is resolved.
/// @param `path` The path of the file or directory to open.
/// @param `oflags` The method by which to open the file.
/// @param `fs_rights_base` The initial rights of the newly created file descriptor. The
/// implementation is allowed to return a file descriptor with fewer rights
/// than specified, if and only if those rights do not apply to the type of
/// file being opened. The *base* rights are rights that will apply to operations
/// using the file descriptor itself.
/// @param `fs_rights_inheriting` The initial rights of the newly created file descriptor. The
/// implementation is allowed to return a file descriptor with fewer rights
/// than specified, if and only if those rights do not apply to the type of
/// file being opened. The *inheriting* rights are rights that apply to
/// file descriptors derived from it.
/// @return The file descriptor of the file that has been opened.
pub fn Fd::path_open(
self : Fd,
dirflags : LookupFlags,
path : String,
oflags : OpenFlags,
fs_rights_base : Rights,
fs_rights_inheriting : Rights,
fs_flags : FdFlags,
) -> Fd raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let fd_ptr = @ffi.malloc(4)
defer @ffi.free(fd_ptr)
let ret = path_open_ffi(
self.0,
dirflags.0,
path_ptr,
path_len,
oflags.0,
fs_rights_base.0,
fs_rights_inheriting.0,
fs_flags.0,
fd_ptr,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let fd = @ffi.load32(fd_ptr)
Fd(fd)
}
///|
fn path_readlink_ffi(
fd : Int,
path : Int,
path_len : Int,
buf : Int,
buf_len : Int,
buf_used : Int,
) -> UInt = "wasi_snapshot_preview1" "path_readlink"
///|
/// Read the contents of a symbolic link.
///
/// Note: This is similar to `readlinkat` in POSIX. If `buf` is not large
/// enough to store the contents of the link, the first `buf_len` bytes will be
/// written `buf`.
pub fn Fd::path_readlink(
self : Fd,
path : String,
buffer? : FixedArray[Byte] = FixedArray::make(4096, 0),
) -> Size raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let buf_len = buffer.length()
let buf_ptr = @ffi.byte_array2ptr(buffer)
defer @ffi.free(buf_ptr)
let buf_used_ptr = @ffi.malloc(4)
defer @ffi.free(buf_used_ptr)
let ret = path_readlink_ffi(
self.0,
path_ptr,
path_len,
buf_ptr,
buf_len,
buf_used_ptr,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let buf_used = @ffi.load32_u(buf_used_ptr)
buf_used
}
///|
fn path_remove_directory_ffi(fd : Int, path : Int, path_len : Int) -> UInt = "wasi_snapshot_preview1" "path_remove_directory"
///|
/// Remove a directory.
///
/// Return `errno::notempty` if the directory is not empty.
///
/// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
pub fn Fd::path_remove_directory(self : Fd, path : String) -> Unit raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let ret = path_remove_directory_ffi(self.0, path_ptr, path_len)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_rename_ffi(
old_fd : Int,
old_path : Int,
old_path_len : Int,
new_fd : Int,
new_path : Int,
new_path_len : Int,
) -> UInt = "wasi_snapshot_preview1" "path_rename"
///|
/// Rename a file or directory.
///
/// Note: This is similar to `renameat` in POSIX.
///
/// @param `old_path` The source path of the file or directory to rename.
/// @param `new_fd` The working directory at which the resolution of the new path starts.
/// @param `new_path` The destination path of the file or directory to rename.
pub fn Fd::path_rename(
self : Fd,
old_path : String,
new_fd : Fd,
new_path : String,
) -> Unit raise Errno {
let old_path_bytes = old_path |> @encoding/utf8.encode()
let old_path_len = old_path_bytes.length()
let old_path_ptr = @ffi.bytes2ptr(old_path_bytes)
defer @ffi.free(old_path_ptr)
let new_path_bytes = new_path |> @encoding/utf8.encode()
let new_path_len = new_path_bytes.length()
let new_path_ptr = @ffi.bytes2ptr(new_path_bytes)
defer @ffi.free(new_path_ptr)
let ret = path_rename_ffi(
self.0,
old_path_ptr,
old_path_len,
new_fd.0,
new_path_ptr,
new_path_len,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_symlink_ffi(
old_path : Int,
old_path_len : Int,
fd : Int,
new_path : Int,
new_path_len : Int,
) -> UInt = "wasi_snapshot_preview1" "path_symlink"
///|
/// Create a symbolic link.
///
/// Note: This is similar to `symlinkat` in POSIX.
pub fn path_symlink(
old_path : String,
fd : Fd,
new_path : String,
) -> Unit raise Errno {
let old_path_bytes = old_path |> @encoding/utf8.encode()
let old_path_len = old_path_bytes.length()
let old_path_ptr = @ffi.bytes2ptr(old_path_bytes)
defer @ffi.free(old_path_ptr)
let new_path_bytes = new_path |> @encoding/utf8.encode()
let new_path_len = new_path_bytes.length()
let new_path_ptr = @ffi.bytes2ptr(new_path_bytes)
defer @ffi.free(new_path_ptr)
let ret = path_symlink_ffi(
old_path_ptr,
old_path_len,
fd.0,
new_path_ptr,
new_path_len,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn path_unlink_file_ffi(fd : Int, path : Int, path_len : Int) -> UInt = "wasi_snapshot_preview1" "path_unlink_file"
///|
/// Unlink a file.
///
/// Return `errno::isdir` if the path refers to a directory.
///
/// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
pub fn Fd::path_unlink_file(self : Fd, path : String) -> Unit raise Errno {
let path_bytes = path |> @encoding/utf8.encode()
let path_len = path_bytes.length()
let path_ptr = @ffi.bytes2ptr(path_bytes)
defer @ffi.free(path_ptr)
let ret = path_unlink_file_ffi(self.0, path_ptr, path_len)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn poll_oneoff_ffi(
in_ : Int,
out : Int,
nsubscriptions : Int,
nevents : Int,
) -> UInt = "wasi_snapshot_preview1" "poll_oneoff"
///|
/// Concurrently poll for the occurrence of a set of events.
///
/// If `nsubscriptions` is 0, returns `errno::inval`.
///
/// @param `in_` The events to which to subscribe.
/// @return The events that have occurred.
pub fn poll_oneoff(in_ : Array[Subscription]) -> Array[Event] raise Errno {
let nsubscriptions = in_.length()
let in_ptr = @ffi.malloc(nsubscriptions * 48)
defer @ffi.free(in_ptr)
let out_ptr = @ffi.malloc(nsubscriptions * 32)
defer @ffi.free(out_ptr)
for i, sub in in_ {
@ffi.store64_u(in_ptr + i * 48, sub.userdata.0)
match sub.u {
Clock({ id, timeout, precision, flags }) => {
@ffi.store32(in_ptr + i * 48 + 8, 0)
@ffi.store32_u(in_ptr + i * 48 + 8 + 8 + 0, id.value())
@ffi.store64_u(in_ptr + i * 48 + 8 + 8 + 8, timeout.0)
@ffi.store64_u(in_ptr + i * 48 + 8 + 8 + 16, precision.0)
@ffi.store32_u(in_ptr + i * 48 + 8 + 8 + 24, flags.0)
}
Fd_read({ file_descriptor }) => {
@ffi.store32(in_ptr + i * 48 + 8, 1)
@ffi.store32(in_ptr + i * 48 + 16, file_descriptor.0)
}
Fd_write({ file_descriptor }) => {
@ffi.store32(in_ptr + i * 48 + 8, 2)
@ffi.store32(in_ptr + i * 48 + 16, file_descriptor.0)
}
}
}
let nevents_ptr = @ffi.malloc(4)
defer @ffi.free(nevents_ptr)
let ret = poll_oneoff_ffi(in_ptr, out_ptr, nsubscriptions, nevents_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let nevents = @ffi.load32(nevents_ptr)
let events = []
for i in 0.. Errno::from_value
|> Option::unwrap
let type_ : EventType = @ffi.load16_u(out_ptr + i * 32 + 10)
|> EventType::from_value
|> Option::unwrap
let nbytes : FileSize = @ffi.load64_u(out_ptr + i * 32 + 16)
let flags : EventRWFlags = @ffi.load32_u(out_ptr + i * 32 + 16 + 8)
let fd_readwrite : Event_fd_readwrite = { nbytes, flags }
events.push(Event::{ userdata, error: errno, type_, fd_readwrite })
}
events
}
///|
fn proc_exit_ffi(rval : UInt) = "wasi_snapshot_preview1" "proc_exit"
///|
/// Terminate the process normally. An exit code of 0 indicates successful
/// termination of the program. The meanings of other values is dependent on
/// the environment.
///
/// Note: the accepted range may depend on the runtime, meaning that it may
/// become an error to return a value outside the accepted range of exit codes,
/// making the actual error code environment-dependent.
pub fn[T] proc_exit(rval : UInt) -> T {
proc_exit_ffi(rval)
panic()
}
///|
fn proc_raise_ffi(sig : UInt) -> UInt = "wasi_snapshot_preview1" "proc_raise"
///|
/// Send a signal to the process of the calling thread.
///
/// Note: This is similar to `raise` in POSIX.
///
/// @param `sig` The signal condition to trigger.
pub fn proc_raise(sig : Signal) -> Unit raise Errno {
let ret = proc_raise_ffi(sig.value())
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn sched_yield_ffi() -> UInt = "wasi_snapshot_preview1" "sched_yield"
///|
/// Temporarily yield execution of the calling thread.
///
/// Note: This is similar to `sched_yield` in POSIX.
pub fn sched_yield() -> Unit raise Errno {
let ret = sched_yield_ffi()
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn random_get_ffi(buf : Int, buf_len : Int) -> UInt = "wasi_snapshot_preview1" "random_get"
///|
/// Write high-quality random data into a buffer.
///
/// This function blocks when the implementaiton is unable to immediately
/// provide sufficient high-quality random data.
///
/// This function may execute slowly, so when larget mounts of random data are
/// required, it's advisable to use this function to seed a pseudo-random
/// number generator, rather than to provide the random data directly.
pub fn random_get(buf : FixedArray[Byte]) -> Unit raise Errno {
let buf_len = buf.length()
let buf_ptr = @ffi.byte_array2ptr(buf)
defer @ffi.free(buf_ptr)
let ret = random_get_ffi(buf_ptr, buf_len)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}
///|
fn sock_accept_ffi(sock : Int, flags : UInt, fd : Int) -> UInt = "wasi_snapshot_preview1" "sock_accept"
///|
/// Accept a new incoming connection on a socket.
///
/// Note: This is similar to `accept` in POSIX.
///
/// @param `self` The listening socket.
/// @param `flags` The desired values of the file descriptor flags.
/// @return New socket connection.
pub fn Fd::sock_accept(self : Fd, flags : FdFlags) -> Fd raise Errno {
let fd_ptr = @ffi.malloc(4)
defer @ffi.free(fd_ptr)
let ret = sock_accept_ffi(self.0, flags.0, fd_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let fd = @ffi.load32(fd_ptr)
Fd(fd)
}
///|
fn sock_recv_ffi(
sock : Int,
ri_data : Int,
ri_data_len : Int,
ri_flags : UInt,
size : Int,
ro_flags : Int,
) -> UInt = "wasi_snapshot_preview1" "sock_recv"
///|
/// Receive a message from a socket.
///
/// Note: This is similar to `recv` in POSIX, though it also supports reading
/// the data into multiple buffers in the manner of `readv`.
///
/// @param `contents` List of scatter/gather vectors to which to store data.
/// @param `flags` Message flags.
/// @return Number of bytes stored in the buffers and message flags.
pub fn Fd::sock_recv(
self : Fd,
contents : Array[FixedArray[Byte]],
flags : RiFlags,
) -> (Size, RoFlags) raise Errno {
let ri_data_len = contents.length()
let ri_data_ptr = @ffi.malloc(ri_data_len * 8)
defer @ffi.free(ri_data_ptr)
for i, byte in contents {
let ptr = @ffi.byte_array2ptr(byte)
@ffi.store32(ri_data_ptr + i * 8, ptr)
@ffi.store32(ri_data_ptr + i * 8 + 4, byte.length())
}
defer (for i in contents {
let ptr = @ffi.byte_array2ptr(i)
@ffi.free(ptr)
})
let ro_flags_ptr = @ffi.malloc(4)
defer @ffi.free(ro_flags_ptr)
let size_ptr = @ffi.malloc(4)
defer @ffi.free(size_ptr)
let ret = sock_recv_ffi(
self.0,
ri_data_ptr,
ri_data_len,
flags.0,
size_ptr,
ro_flags_ptr,
)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let size = @ffi.load32_u(size_ptr)
let ro_flags = @ffi.load32_u(ro_flags_ptr)
(size, ro_flags)
}
///|
fn sock_send_ffi(
sock : Int,
si_data : Int,
si_data_len : Int,
si_flags : UInt,
size : Int,
) -> UInt = "wasi_snapshot_preview1" "sock_send"
///|
/// Send a message on a socket.
///
/// Note: This is similar to `send` in POSIX, though it also supports writing
/// the data from multiple buffers in the manner of `writev`.
///
/// @param `contents` List of scatter/gather vectors to which to retrieve the data.
/// @param `flags` Message flags.
/// @return The number of bytes transmitted.
pub fn Fd::sock_send(
self : Fd,
contents : Array[BytesView],
flags : SiFlags,
) -> Size raise Errno {
let si_data_len = contents.length()
let si_data_ptr = @ffi.malloc(si_data_len * 8)
defer @ffi.free(si_data_ptr)
for i, byte in contents {
let ptr = @ffi.bytes2ptr(byte.data())
@ffi.store32(si_data_ptr + i * 8, ptr + byte.start_offset())
@ffi.store32(si_data_ptr + i * 8 + 4, byte.length())
}
defer (for i in contents {
let ptr = @ffi.bytes2ptr(i.data())
@ffi.free(ptr)
@ffi.free(ptr)
})
let size_ptr = @ffi.malloc(4)
defer @ffi.free(size_ptr)
let ret = sock_send_ffi(self.0, si_data_ptr, si_data_len, flags.0, size_ptr)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
let size = @ffi.load32_u(size_ptr)
size
}
///|
fn sock_shutdown_ffi(sock : Int, how : UInt) -> UInt = "wasi_snapshot_preview1" "sock_shutdown"
///|
/// Shut down socket send and receive channels.
///
/// Note: This is similar to `shutdown` in POSIX.
///
/// @param `how` Which channels on the socket to shut down.
pub fn Fd::sock_shutdown(self : Fd, how : SdFlags) -> Unit raise Errno {
let ret = sock_shutdown_ffi(self.0, how.0)
let ret = Errno::from_value(ret).unwrap()
if ret != Success {
raise ret
}
}