Skip to content

Commit

Permalink
os/os2: wasi target support
Browse files Browse the repository at this point in the history
  • Loading branch information
laytan committed Jan 18, 2025
1 parent 4703050 commit e4892f1
Show file tree
Hide file tree
Showing 17 changed files with 1,238 additions and 4 deletions.
1 change: 1 addition & 0 deletions core/crypto/rand_generic.odin
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#+build !netbsd
#+build !darwin
#+build !js
#+build !wasi
package crypto

HAS_RAND_BYTES :: false
Expand Down
13 changes: 13 additions & 0 deletions core/crypto/rand_wasi.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package crypto

import "core:fmt"
import "core:sys/wasm/wasi"

HAS_RAND_BYTES :: true

@(private)
_rand_bytes :: proc(dst: []byte) {
if err := wasi.random_get(dst); err != nil {
fmt.panicf("crypto: wasi.random_get failed: %v", err)
}
}
9 changes: 6 additions & 3 deletions core/os/os2/dir_posix.odin
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}

n := len(fimpl.name)+1
non_zero_resize(&it.impl.fullpath, n+len(sname))
n += copy(it.impl.fullpath[n:], sname)
if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
// Can't really tell caller we had an error, sad.
return
}
copy(it.impl.fullpath[n:], sname)

fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
Expand All @@ -60,7 +63,7 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
iter.f = f
iter.impl.idx = 0

iter.impl.fullpath.allocator = file_allocator()
iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
append(&iter.impl.fullpath, impl.name)
append(&iter.impl.fullpath, "/")
defer if err != nil { delete(iter.impl.fullpath) }
Expand Down
110 changes: 110 additions & 0 deletions core/os/os2/dir_wasi.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#+private
package os2

import "base:intrinsics"
import "core:sys/wasm/wasi"

Read_Directory_Iterator_Impl :: struct {
fullpath: [dynamic]byte,
buf: []byte,
off: int,
idx: int,
}

@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
fimpl := (^File_Impl)(it.f.impl)

buf := it.impl.buf[it.impl.off:]

index = it.impl.idx
it.impl.idx += 1

for {
if len(buf) < size_of(wasi.dirent_t) {
return
}

entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
buf = buf[size_of(wasi.dirent_t):]

if len(buf) < int(entry.d_namlen) {
// shouldn't be possible.
return
}

name := string(buf[:entry.d_namlen])
buf = buf[entry.d_namlen:]
it.impl.off += size_of(wasi.dirent_t) + int(entry.d_namlen)

if name == "." || name == ".." {
continue
}

n := len(fimpl.name)+1
if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
// Can't really tell caller we had an error, sad.
return
}
copy(it.impl.fullpath[n:], name)

stat, err := wasi.path_filestat_get(__fd(it.f), {}, name)
if err != nil {
// Can't stat, fill what we have from dirent.
stat = {
ino = entry.d_ino,
filetype = entry.d_type,
}
}

fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
return
}
}

@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_File
return
}

impl := (^File_Impl)(f.impl)
iter.f = f

buf: [dynamic]byte
buf.allocator = file_allocator()
defer if err != nil { delete(buf) }

// NOTE: this is very grug.
for {
non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2) or_return

n, _err := wasi.fd_readdir(__fd(f), buf[:], 0)
if _err != nil {
err = _get_platform_error(_err)
return
}

if n < len(buf) {
non_zero_resize(&buf, n)
break
}

assert(n == len(buf))
}
iter.impl.buf = buf[:]

iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
append(&iter.impl.fullpath, impl.name)
append(&iter.impl.fullpath, "/")

return
}

_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
delete(it.impl.buf, file_allocator())
delete(it.impl.fullpath)
it^ = {}
}
186 changes: 186 additions & 0 deletions core/os/os2/env_wasi.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#+private
package os2

import "base:runtime"

import "core:strings"
import "core:sync"
import "core:sys/wasm/wasi"

g_env: map[string]string
g_env_buf: []byte
g_env_mutex: sync.RW_Mutex
g_env_error: Error
g_env_built: bool

build_env :: proc() -> (err: Error) {
if g_env_built || g_env_error != nil {
return g_env_error
}

sync.guard(&g_env_mutex)

if g_env_built || g_env_error != nil {
return g_env_error
}

defer if err != nil {
g_env_error = err
}

num_envs, size_of_envs, _err := wasi.environ_sizes_get()
if _err != nil {
return _get_platform_error(_err)
}

g_env = make(map[string]string, num_envs, file_allocator()) or_return
defer if err != nil { delete(g_env) }

g_env_buf = make([]byte, size_of_envs, file_allocator()) or_return
defer if err != nil { delete(g_env_buf, file_allocator()) }

TEMP_ALLOCATOR_GUARD()

envs := make([]cstring, num_envs, temp_allocator()) or_return

_err = wasi.environ_get(raw_data(envs), raw_data(g_env_buf))
if _err != nil {
return _get_platform_error(_err)
}

for env in envs {
key, _, value := strings.partition(string(env), "=")
g_env[key] = value
}

g_env_built = true
return
}

delete_string_if_not_original :: proc(str: string) {
start := uintptr(raw_data(g_env_buf))
end := start + uintptr(len(g_env_buf))
ptr := uintptr(raw_data(str))
if ptr < start || ptr > end {
delete(str, file_allocator())
}
}

@(require_results)
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if err := build_env(); err != nil {
return
}

sync.shared_guard(&g_env_mutex)

value = g_env[key] or_return
value, _ = clone_string(value, allocator)
return
}

@(require_results)
_set_env :: proc(key, value: string) -> bool {
if err := build_env(); err != nil {
return false
}

sync.guard(&g_env_mutex)

key_ptr, value_ptr, just_inserted, err := map_entry(&g_env, key)
if err != nil {
return false
}

alloc_err: runtime.Allocator_Error

if just_inserted {
key_ptr^, alloc_err = clone_string(key, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
return false
}

value_ptr^, alloc_err = clone_string(value, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
delete(key_ptr^, file_allocator())
return false
}

return true
}

delete_string_if_not_original(value_ptr^)

value_ptr^, alloc_err = clone_string(value, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
return false
}

return true
}

@(require_results)
_unset_env :: proc(key: string) -> bool {
if err := build_env(); err != nil {
return false
}

sync.guard(&g_env_mutex)

dkey, dval := delete_key(&g_env, key)
delete_string_if_not_original(dkey)
delete_string_if_not_original(dval)
return true
}

_clear_env :: proc() {
sync.guard(&g_env_mutex)

for k, v in g_env {
delete_string_if_not_original(k)
delete_string_if_not_original(v)
}

delete(g_env_buf, file_allocator())
g_env_buf = {}

clear(&g_env)

g_env_built = true
}

@(require_results)
_environ :: proc(allocator: runtime.Allocator) -> []string {
if err := build_env(); err != nil {
return nil
}

sync.shared_guard(&g_env_mutex)

envs, alloc_err := make([]string, len(g_env), allocator)
if alloc_err != nil {
return nil
}

defer if alloc_err != nil {
for env in envs {
delete(env, allocator)
}
delete(envs, allocator)
}

i: int
for k, v in g_env {
defer i += 1

envs[i], alloc_err = concatenate({k, "=", v}, allocator)
if alloc_err != nil {
return nil
}
}

return envs
}
47 changes: 47 additions & 0 deletions core/os/os2/errors_wasi.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#+private
package os2

import "base:runtime"

import "core:slice"
import "core:sys/wasm/wasi"

_Platform_Error :: wasi.errno_t

_error_string :: proc(errno: i32) -> string {
e := wasi.errno_t(errno)
if e == .NONE {
return ""
}

err := runtime.Type_Info_Enum_Value(e)

ti := &runtime.type_info_base(type_info_of(wasi.errno_t)).variant.(runtime.Type_Info_Enum)
if idx, ok := slice.binary_search(ti.values, err); ok {
return ti.names[idx]
}
return "<unknown platform error>"
}

_get_platform_error :: proc(errno: wasi.errno_t) -> Error {
#partial switch errno {
case .PERM:
return .Permission_Denied
case .EXIST:
return .Exist
case .NOENT:
return .Not_Exist
case .TIMEDOUT:
return .Timeout
case .PIPE:
return .Broken_Pipe
case .BADF:
return .Invalid_File
case .NOMEM:
return .Out_Of_Memory
case .NOSYS:
return .Unsupported
case:
return Platform_Error(errno)
}
}
Loading

0 comments on commit e4892f1

Please sign in to comment.