#![no_std]
#[macro_use]
extern crate alloc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
pub struct EnvironmentalVariable {
pub name: String,
pub value: String,
}
pub struct AccessibleDirectory {
pub path: String,
pub fd: u32,
}
pub fn read_line() -> String {
let mut line = read_str(0);
let len = line.len();
line.truncate(len - 1);
line
}
pub fn print(message: &str) {
write_str(1, message)
}
fn write_str(fd: u32, s: &str) {
write_all_bytes(fd, s.as_bytes())
}
fn write_all_bytes(fd: u32, s: &[u8]) {
unsafe {
let data = [wasi::Ciovec {
buf: s.as_ptr(),
buf_len: s.len(),
}];
wasi::fd_write(fd, &data).unwrap();
}
}
fn read_str(fd: u32) -> String {
let path_data = read_all_bytes(fd);
String::from_utf8_lossy(&path_data)
.trim_end_matches("\0")
.to_string()
}
fn read_all_bytes(fd: u32) -> Vec<u8> {
unsafe {
let len = wasi::fd_filestat_get(fd).unwrap().size as usize;
let mut path_data: Vec<u8> = vec![0; len];
let data = [wasi::Iovec {
buf: path_data.as_mut_ptr(),
buf_len: path_data.len(),
}];
wasi::fd_read(fd, &data).unwrap();
path_data
}
}
pub fn println(message: &str) {
let mut m = String::from(message);
m.push_str("\n");
print(&m);
}
pub fn error(message: &str) {
let mut m = String::from(message);
m.push_str("\n");
write_str(2, &m)
}
pub fn command_line_arguments() -> Vec<String> {
let mut cmd_args: Vec<String> = vec![];
unsafe {
let (ct, len) = wasi::args_sizes_get().unwrap();
let mut cmd_arg_positions: Vec<i32> = vec![0; ct];
let mut cmd_arg_data: Vec<u8> = vec![0; len];
wasi::args_get(
cmd_arg_positions.as_mut_ptr() as *mut *mut u8,
cmd_arg_data.as_mut_ptr() as *mut u8,
)
.unwrap();
for x in 0..ct {
let base = cmd_arg_data.as_mut_ptr() as usize;
let beg = cmd_arg_positions[x] as usize - base;
let end = if x == ct - 1 {
cmd_arg_data.len() - 1
} else {
cmd_arg_positions[x + 1] as usize - 1 - base
};
let cmd_arg = String::from_utf8_lossy(&cmd_arg_data[beg..end]);
cmd_args.push(cmd_arg.to_string());
}
}
cmd_args
}
pub fn environment_variables() -> Vec<EnvironmentalVariable> {
let mut envs: Vec<EnvironmentalVariable> = vec![];
unsafe {
let (ct, len) = wasi::environ_sizes_get().unwrap();
let mut env_positions: Vec<i32> = vec![0; ct];
let mut env_data: Vec<u8> = vec![0; len];
wasi::environ_get(
env_positions.as_mut_ptr() as *mut *mut u8,
env_data.as_mut_ptr() as *mut u8,
)
.unwrap();
for x in 0..ct {
let base = env_data.as_mut_ptr() as usize;
let beg = env_positions[x] as usize - base;
let end = if x == ct - 1 {
env_data.len() - 1
} else {
env_positions[x + 1] as usize - 1 - base
};
let pair = String::from_utf8_lossy(&env_data[beg..end]);
let p: Vec<&str> = pair.splitn(2, '=').collect();
envs.push(EnvironmentalVariable {
name: p[0].to_string(),
value: p[1].to_string(),
});
}
}
envs
}
pub fn random_number() -> f32 {
unsafe {
let mut noise: Vec<u8> = vec![0; 8];
let max = 0xFFFFFFFFFFFFFFFF as u64;
let mut array = [0 as u8; 8];
wasi::random_get(noise.as_mut_ptr() as *mut u8, 8).unwrap();
array.copy_from_slice(&noise);
let value = u64::from_be_bytes(array);
value as f32 / max as f32
}
}
pub fn sleep(millis: usize) {
unsafe {
let t = current_time();
loop {
wasi::sched_yield().unwrap();
let nt = current_time();
if nt - t >= millis as u64 {
break;
}
}
}
}
pub fn exit() -> ! {
unsafe {
wasi::proc_exit(0);
panic!("should not be here")
}
}
pub fn high_precision_time() -> u64 {
unsafe {
let precision = 0;
wasi::clock_time_get(wasi::CLOCKID_REALTIME, precision).unwrap() as u64
}
}
pub fn current_time() -> u64 {
unsafe {
let precision = 0;
wasi::clock_time_get(wasi::CLOCKID_REALTIME, precision).unwrap() as u64 / 1000000
}
}
pub fn unix_time() -> u64 {
unsafe {
let precision = 0;
wasi::clock_time_get(wasi::CLOCKID_REALTIME, precision).unwrap() as u64 / 1000000000
}
}
pub fn read_text(path: &str) -> Result<String, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, false)?;
let data = read_str(fd);
close_file(fd);
Ok(data)
} else {
Err("no access to file".to_string())
}
}
pub fn read_bytes(path: &str) -> Result<Vec<u8>, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, false)?;
let data = read_all_bytes(fd);
close_file(fd);
Ok(data)
} else {
Err("no access to file".to_string())
}
}
fn open_file(dir_fd: u32, relative_path: &str, write: bool) -> Result<u32, String> {
let mut oflags = 0;
if write {
oflags |= wasi::OFLAGS_CREAT;
oflags |= wasi::OFLAGS_TRUNC;
}
unsafe {
match wasi::path_open(
dir_fd,
0,
relative_path,
oflags,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0,
) {
Ok(fd) => Ok(fd),
Err(_) => Err("Something went wrong opening file, did you give access?".to_string()),
}
}
}
fn close_file(fd: u32) {
unsafe { wasi::fd_close(fd).unwrap() }
}
fn relative_path(base_dir: &str, path: &str) -> String {
let rel_path = &path[base_dir.len()..];
rel_path.to_string()
}
pub fn write_text(path: &str, data: &str) -> Result<(), String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, true)?;
write_str(fd, data);
close_file(fd);
Ok(())
} else {
Err("no access to file".to_string())
}
}
pub fn write_bytes(path: &str, data: &[u8]) -> Result<(), String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, true)?;
write_all_bytes(fd, data);
close_file(fd);
Ok(())
} else {
Err("no access to file".to_string())
}
}
pub fn accessible_directories() -> Vec<AccessibleDirectory> {
let mut dirs = vec![];
let mut i = 3;
loop {
let path = unsafe {
let len = 1024;
let mut path_data: Vec<u8> = vec![0; len];
if let Ok(_) = wasi::fd_prestat_dir_name(i, path_data.as_mut_ptr() as *mut u8, len) {
let mut p = String::from_utf8_lossy(&path_data)
.trim_end_matches("\0")
.to_string();
if !p.ends_with("/") {
p.push_str("/");
}
p
} else {
break;
}
};
dirs.push(AccessibleDirectory {
fd: i,
path: path.to_string(),
});
i = i + 1;
}
dirs
}
fn get_owning_directory(path: &str) -> Option<AccessibleDirectory> {
let mut d = None;
let dirs = accessible_directories();
let mut len = 0;
for dir in dirs.into_iter() {
let dir_path_len = dir.path.len();
if path.starts_with(&dir.path) && dir_path_len > len {
d = Some(dir);
len = dir_path_len
}
}
d
}
pub fn file_size(path: &str) -> Result<u64, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let file_stat = unsafe { wasi::path_filestat_get(parent_dir.fd, 0, &rel_path).unwrap() };
Ok(file_stat.size)
} else {
Err("no access to file".to_string())
}
}
pub fn is_readable(path: &str) -> Result<bool, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, false).unwrap();
let fd_stat = unsafe { wasi::fd_fdstat_get(fd).unwrap() };
Ok((fd_stat.fs_rights_base & wasi::RIGHTS_FD_READ) != 0)
} else {
Err("no access to file".to_string())
}
}
pub fn is_writeable(path: &str) -> Result<bool, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let fd = open_file(parent_dir.fd, &rel_path, false).unwrap();
let fd_stat = unsafe { wasi::fd_fdstat_get(fd).unwrap() };
Ok((fd_stat.fs_rights_base & wasi::RIGHTS_FD_WRITE) != 0)
} else {
Err("no access to file".to_string())
}
}
pub fn is_directory(path: &str) -> Result<bool, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let file_stat = unsafe { wasi::path_filestat_get(parent_dir.fd, 0, &rel_path).unwrap() };
Ok(file_stat.filetype == wasi::FILETYPE_DIRECTORY)
} else {
Err("no access to file".to_string())
}
}
pub fn is_stream(path: &str) -> Result<bool, String> {
if let Some(parent_dir) = get_owning_directory(path) {
let rel_path = relative_path(&parent_dir.path, path);
let file_stat = unsafe { wasi::path_filestat_get(parent_dir.fd, 0, &rel_path).unwrap() };
Ok(file_stat.filetype == wasi::FILETYPE_CHARACTER_DEVICE)
} else {
Err("no access to file".to_string())
}
}