Supports_Os :: true
Supports_Processes :: false
Supports_Time :: true
-Supports_Networking :: false
Supports_Type_Info :: true
Supports_Threads :: true
Supports_Env_Vars :: true
-Supports_Futexes :: false
-Supports_TTY :: false
+#if #defined(runtime.vars.WASIX) {
+ Supports_Networking :: true
+ Supports_Futexes :: true
+
+ #load "./wasix_defs"
+ #load "./wasix_net"
+ #load "./wasix_misc"
+
+} else {
+ Supports_Networking :: false
+ Supports_Futexes :: false
+}
+
+Supports_TTY :: false
__output_string :: (s: str) -> u32 {
STDOUT_FILENO :: 1
}
__wait_for_input :: (timeout: i32) -> bool {
+ STDIN_FILENO :: 0
+
events := 1;
subscriptions: [2] Subscription;
fd_tagged: SubscriptionTagged;
fd_tagged.tag = .FDRead;
- fd_tagged.fd_read = .{ file_description = STDIN_FILENO, };
+ fd_tagged.fd_read = .{ file_descriptor = STDIN_FILENO };
subscriptions[0] = .{
userdata = 0,
- u = tagged,
+ u = fd_tagged,
};
clock_tagged: SubscriptionTagged;
clock_tagged.clock = .{
id = .Realtime,
timeout = cast(u64) timeout * 1000000,
- precision
+ precision = 1,
};
subscriptions[1] = .{
event: [2] Event;
number_of_events: u32;
- error_code := poll_oneoff(&subscription, &event, 2, &number_of_events);
+ error_code := poll_oneoff(~~subscriptions, ~~event, 2, &number_of_events);
if number_of_events == 0 do return false;
if event[0].type == .Clock do return false;
random_get(buf.data, buf.length);
}
+PollDescription :: struct {
+ fd: wasi.FileDescriptor;
+ in: core.io.PollEvent;
+ out: core.io.PollEvent;
+}
+
+__poll :: (fds: [] PollDescription, timeout: i32) -> void {
+ count := fds.count;
+ if timeout == 0 {
+ timeout = 1;
+ }
+
+ if timeout > 0 {
+ count += 1;
+ }
+
+ subs := core.alloc.array_from_stack(wasi.Subscription, count);
+ for i: fds.count {
+ subs[i].userdata = ~~ i;
+ subs[i].u.tag = switch fds[i].in {
+ case .None => .FDRead;
+ case .Read => .FDRead;
+ case .Write => .FDWrite;
+ case .Closed => .FDRead;
+ };
+ subs[i].u.u.fd_read = .{ file_descriptor = cast(i32) fds[i].fd };
+ }
+
+ if timeout > 0 {
+ subs[count - 1].userdata = 0;
+ subs[count - 1].u.tag = .Clock;
+ subs[count - 1].u.u.clock = .{
+ id = .Realtime,
+ timeout = cast(u64) timeout * 1000000,
+ precision = 1,
+ };
+ }
+
+ events := core.alloc.array_from_stack(wasi.Event, count);
+ number_of_events: u32;
+
+ error_code := wasi.poll_oneoff(~~subs.data, ~~events.data, count, &number_of_events);
+ if error_code != .Success do return;
+
+ if number_of_events == 0 do return;
+ for ev: events[0 .. number_of_events] {
+ if ev.type !=.Clock {
+ if ev.fd_readwrite.nbytes > 0 {
+ i := cast(i32) ev.userdata;
+ fds[i].out = fds[i].in;
+ }
+ }
+ }
+}
+
// Sets up everything needed for execution.
__start :: () {
return ~~ file_stat.size;
},
- poll = (use fs: &os.File, ev: io.PollEvent, timeout: i32) -> (Error, bool) {
- // TODO: Not entirely sure this is implemented correctly.
- events := 1;
- subscriptions: [2] Subscription;
-
- fd_tagged: SubscriptionTagged;
- fd_tagged.tag = .FDRead;
- fd_tagged.fd_read = .{ file_description = cast(i64) data, };
- subscriptions[0] = .{
- userdata = 0,
- u = tagged,
- };
-
- clock_tagged: SubscriptionTagged;
- if timeout > 0 {
- events += 1;
- clock_tagged.tag = .Clock;
- clock_tagged.clock = .{
- id = .Realtime,
- timeout = cast(u64) timeout * 1000000,
- precision
- };
-
- subscriptions[1] = .{
- userdata = 0,
- u = clock_tagged,
- };
- }
-
- event: [2] Event;
- number_of_events: u32;
-
- error_code := poll_oneoff(&subscription, &event, 2, &number_of_events);
+ poll = (use fs: &os.File, ev: io.PollEvent, timeout: i32) -> (io.Error, bool) {
+ p: [1] PollDescription = .[.{
+ fd = data.fd,
+ in = ev,
+ }];
- if number_of_events == 0 do return .None, false;
- if event[0].type == .Clock do return .None, false;
+ runtime.platform.__poll(p, timeout);
- return .None, event[0].fd_readwrite.nbytes > 0;
+ return .None, p[0].out == ev;
}
}
--- /dev/null
+package wasi
+
+#if !#defined(WASIX_VERSION) {
+ WASIX_VERSION :: "wasix_32v1"
+}
+
+EventFDFlags :: enum {
+}
+
+TTY :: struct {
+ cols: u32;
+ rows: u32;
+ width: u32;
+ height: u32;
+ stdin_tty: bool;
+ stdout_tty: bool;
+ stderr_tty: bool;
+ echo: bool;
+ line_buffered: bool;
+ line_feeds: bool;
+}
+
+OptionTimestamp :: union #tag_type u8 {
+ None: void;
+ Some: Timestamp;
+}
+
+SockStatus :: enum {
+ Opening;
+ Opened;
+ Closed;
+ Failed;
+}
+
+AddrIPv4 :: struct {
+ n0, n1, h0, h1: u8;
+}
+
+AddrIPv6 :: struct {
+ n0, n1, n2, n3, h0, h1, h2, h3: u8;
+ flow_info1: u16;
+ flow_info0: u16;
+ scope_id1: u16;
+ scope_id0: u16;
+}
+
+Addr :: union #tag_type u8 {
+ Unspec: struct {
+ n0: u8;
+ };
+
+ Ipv4: AddrIPv4;
+
+ Ipv6: AddrIPv6;
+
+ Unix: [108] u8;
+}
+
+AddrPort :: union #tag_type u8 {
+ Unspec: struct {
+ port: u16;
+ n0: u8;
+ };
+
+ Ipv4: struct {
+ port: u16;
+ addr: AddrIPv4;
+ };
+
+ Ipv6: struct {
+ port: u16;
+ addr: AddrIPv6;
+ };
+
+ Unix: struct {
+ path: [108] u8;
+ }
+}
+
+AddressFamily :: enum {
+ Unspec;
+ Inet;
+ Inet6;
+ Unix;
+}
+
+SockType :: enum {
+ Unused;
+ Stream;
+ Dgram;
+ Raw;
+ Seqpacket;
+}
+
+SockProto :: enum {
+ IP;
+ ICMP;
+ IGMP;
+ IPIP :: 4;
+ TCP :: 6;
+ EGP :: 8;
+ PUP :: 12;
+ UDP :: 17;
+ IDP :: 22;
+ DCCP :: 33;
+ IPV6 :: 41;
+ ICMPV6 :: 58;
+}
+
+SockOption :: enum {
+ Noop;
+ ReusePort;
+ ReuseAddr;
+ NoDelay;
+ DontRoute;
+ OnlyV6;
+ Broadcast;
+ MulticastLoopV4;
+ MulticastLoopV6;
+ Promiscuous;
+ Listening;
+ LastError;
+ KeepAlive;
+ Linger;
+ OOBInline;
+ RecvBufSize;
+ SendBufSize;
+ RecvLowat;
+ SendLowat;
+ RecvTimeout;
+ SendTimeout;
+ ConnectTimeout;
+ AcceptTimeout;
+ TTL;
+ MulticastTTLV5;
+ Type;
+ Proto;
+}
+
+#foreign WASIX_VERSION {
+ fd_dup :: (fd: FileDescriptor, out: &FileDescriptor) -> Errno ---
+ fd_event :: (init_val: u64, flags: EventFDFlags, fd: &FileDescriptor) -> Errno ---
+ fd_pipe :: (fd1, fd2: &FileDescriptor) -> Errno ---
+
+ tty_get :: (state: &TTY) -> Errno ---
+ tty_set :: (state: &TTY) -> Errno ---
+
+ getcwd :: (path: [&] u8, pathlen: &u32) -> Errno ---
+ chdir :: (path: cstr) -> Errno ---
+
+ thread_spawn_v2 :: (args: rawptr, tid: &u32) -> Errno ---
+ thread_sleep :: (duration: Timestamp) -> Errno ---
+ thread_id :: (tid: &u32) -> Errno ---
+ thread_join :: (tid: u32) -> Errno ---
+ thread_parallelism :: (size: &u32) -> Errno ---
+
+ futex_wait :: (futex: &u32, expected: u32, timeout: &OptionTimestamp, out: &bool) -> Errno ---
+ futex_wake :: (futex: &u32, out: &bool) -> Errno ---
+ futex_wake_all :: (futex: &u32, out: &bool) -> Errno ---
+
+ sock_status :: (fd: FileDescriptor, out: &SockStatus) -> Errno ---
+ sock_addr_local :: (fd: FileDescriptor, addr: &AddrPort) -> Errno ---
+ sock_addr_peer :: (fd: FileDescriptor, addr: &AddrPort) -> Errno ---
+ sock_open :: (af: AddressFamily, socktype: SockType, sockproto: SockProto, out: &FileDescriptor) -> Errno ---
+ sock_set_opt_flag :: (fd: FileDescriptor, sockopt: SockOption, flag: bool) -> Errno ---
+ sock_get_opt_flag :: (fd: FileDescriptor, sockopt: SockOption, flag: &bool) -> Errno ---
+ sock_set_opt_time :: (fd: FileDescriptor, sockopt: SockOption, flag: &OptionTimestamp) -> Errno ---
+ sock_get_opt_time :: (fd: FileDescriptor, sockopt: SockOption, flag: &OptionTimestamp) -> Errno ---
+ sock_set_opt_size :: (fd: FileDescriptor, sockopt: SockOption, flag: u64) -> Errno ---
+ sock_get_opt_size :: (fd: FileDescriptor, sockopt: SockOption, flag: &u64) -> Errno ---
+ sock_bind :: (fd: FileDescriptor, addr: &AddrPort) -> Errno ---
+ sock_listen :: (fd: FileDescriptor, backlog: i32) -> Errno ---
+ sock_accept_v2 :: (fd: FileDescriptor, flags: FDFlags, out_sock: &FileDescriptor, out_addr: &AddrPort) -> Errno ---
+ sock_connect :: (fd: FileDescriptor, addr: &AddrPort) -> Errno ---
+ sock_recv_from :: (fd: FileDescriptor, data: &IOVec, data_len: Size, flags: RIFlags, written: &Size, oflags: &ROFlags, addr: &AddrPort) -> Errno ---
+ sock_send_to :: (fd: FileDescriptor, data: &IOVec, data_len: Size, flags: SIFlags, addr: &AddrPort, sent: &Size) -> Errno ---
+
+ resolve :: (host: cstr, port: u16, addr: [&] Addr, addr_len: Size, retaddrs: &Size) -> Errno ---
+}
\ No newline at end of file
--- /dev/null
+package runtime.platform
+
+use wasi
+
+__futex_wait :: (addr: rawptr, expected: i32, timeout: i32) -> i32 {
+ tm: wasi.OptionTimestamp;
+ if timeout < 0 {
+ tm = .{ None = .{} };
+ } else {
+ tm = .{ Some = ~~timeout };
+ }
+
+ out: bool;
+ if wasi.futex_wait(addr, expected, &tm, &out) != .Success do return 0;
+ return 1 if out else 0;
+}
+
+__futex_wake :: (addr: rawptr, maximum: i32) -> i32 {
+ out: bool;
+ if (maximum == -1) {
+ if wasi.futex_wake_all(addr, &out) != .Success do return 0;
+ } else {
+ if wasi.futex_wake(addr, &out) != .Success do return 0;
+ }
+
+ return 1 if out else 0;
+}
\ No newline at end of file
--- /dev/null
+package runtime.platform
+
+use wasi
+use core.net {
+ SocketFamily,
+ SocketProto,
+ SocketType,
+ SocketOption,
+ SocketAddress,
+ SocketShutdown,
+ SocketStatus
+}
+use core {Result, string, io}
+
+SocketData :: wasi.FileDescriptor
+
+__net_sock_create :: (af: SocketFamily, type: SocketType, proto: SocketProto) -> Result(SocketData, io.Error) {
+ family := cast(wasi.AddressFamily) cast(u32) af;
+ socktype := switch type {
+ case .Stream => wasi.SockType.Stream;
+ case .Dgram => wasi.SockType.Dgram;
+ };
+ sockproto := cast(wasi.SockProto) cast(u32) proto;
+
+ sock: wasi.FileDescriptor;
+ err := wasi.sock_open(family, socktype, sockproto, &sock);
+
+ if err == .Success {
+ return .{ Ok = sock };
+ }
+
+ return .{ Err = .CreationFailed };
+}
+
+__net_sock_status :: (s: SocketData) -> SocketStatus {
+ status: wasi.SockStatus;
+ if wasi.sock_status(s, &status) != .Success {
+ return .Errored;
+ }
+
+ return switch status {
+ case .Opening => .Opening;
+ case .Opened => .Open;
+ case .Closed => .Closed;
+ case .Failed => .Errored;
+ };
+}
+
+__net_sock_opt_flag :: (s: SocketData, sockopt: SocketOption, flag: bool) -> bool {
+ if sockopt == .NonBlocking {
+ return wasi.fd_fdstat_set_flags(s, wasi.FDFlags.NonBlock if flag else 0) == .Success;
+ }
+
+ opt := switch sockopt {
+ case .Broadcast => wasi.SockOption.Broadcast;
+ case .ReuseAddress => wasi.SockOption.ReuseAddr;
+ case #default => wasi.SockOption.Noop;
+ };
+ return wasi.sock_set_opt_flag(s, opt, flag) == .Success;
+}
+
+__net_sock_opt_time :: (s: SocketData, sockopt: SocketOption, time: ? u64) -> bool {
+ return false;
+}
+
+__net_sock_opt_size :: (s: SocketData, sockopt: SocketOption, size: i64) -> bool {
+ return false;
+}
+
+__net_sock_bind :: (s: SocketData, addr: &SocketAddress) -> bool {
+ target: wasi.AddrPort;
+ socket_addr_to_wasix_addr(addr, &target);
+
+ return wasi.sock_bind(s, &target) == .Success;
+}
+
+__net_sock_listen :: (s: SocketData, backlog: i32) -> bool {
+ return wasi.sock_listen(s, backlog) == .Success;
+}
+
+__net_sock_accept :: (s: SocketData, out: &SocketAddress) -> Result(SocketData, io.Error) {
+ new_sock: SocketData;
+ addr: wasi.AddrPort;
+ switch wasi.sock_accept_v2(s, 0, &new_sock, &addr) {
+ case .Success ---
+ case .Again do return .{ Err = .NoData };
+ case #default do return .{ Err = .OperationFailed };
+ }
+
+ wasi_addr_to_socket_address(&addr, out);
+
+ return .{ Ok = new_sock };
+}
+
+__net_sock_connect :: (s: SocketData, addr: &SocketAddress) -> io.Error {
+ target: wasi.AddrPort;
+ socket_addr_to_wasix_addr(addr, &target);
+
+ return switch wasi.sock_connect(s, &target) {
+ case .Success => .None;
+ case .ConnRefused,
+ .ConnAborted,
+ .ConnReset
+ => .ConnectFailed;
+
+ case #default => .OperationFailed;
+ };
+}
+
+__net_sock_recv_from :: (s: SocketData, buf: [] u8, out: &SocketAddress) -> Result(i32, io.Error) {
+ vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count };
+
+ out_len: u32;
+ out_flags: wasi.ROFlags;
+ sender: wasi.AddrPort;
+ return switch wasi.sock_recv_from(s, &vec, 1, 0, &out_len, &out_flags, &sender) {
+ case .Success => do {
+ wasi_addr_to_socket_address(&sender, out);
+ return Result(i32, io.Error).{ Ok = out_len };
+ };
+ case .Again => .{ Err = .NoData };
+ case #default => .{ Err = .EOF };
+ };
+}
+
+__net_sock_send_to :: (s: SocketData, buf: [] u8, addr: &SocketAddress) -> Result(i32, io.Error) {
+ vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count };
+
+ out_len: u32;
+ target: wasi.AddrPort;
+ socket_addr_to_wasix_addr(addr, &target);
+ return switch wasi.sock_send_to(s, &vec, 1, 0, &target, &out_len) {
+ case .Success => Result(i32, io.Error).{ Ok = out_len };
+ case .Again => Result(i32, io.Error).{ Err = .NoData };
+ case #default => Result(i32, io.Error).{ Err = .EOF };
+ };
+}
+
+__net_sock_recv :: (s: SocketData, buf: [] u8) -> Result(i32, io.Error) {
+ vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count };
+
+ out_len: u32;
+ out_flags: wasi.ROFlags;
+ return switch wasi.sock_recv(s, &vec, 1, 0, &out_len, &out_flags) {
+ case .Success => Result(i32, io.Error).{ Ok = out_len };
+ case .Again => Result(i32, io.Error).{ Err = .NoData };
+ case #default => Result(i32, io.Error).{ Err = .EOF };
+ };
+}
+
+__net_sock_send :: (s: SocketData, buf: [] u8) -> Result(i32, io.Error) {
+ vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count };
+
+ out_len: u32;
+ return switch wasi.sock_send(s, &vec, 1, 0, &out_len) {
+ case .Success => Result(i32, io.Error).{ Ok = out_len };
+ case .Again => Result(i32, io.Error).{ Err = .NoData };
+ case #default => Result(i32, io.Error).{ Err = .EOF };
+ };
+}
+
+__net_sock_shutdown :: (s: SocketData, how: SocketShutdown) -> io.Error {
+ sd := switch how {
+ case .Read => wasi.SDFlags.RD;
+ case .Write => wasi.SDFlags.WR;
+ case .ReadWrite => wasi.SDFlags.RD | wasi.SDFlags.WR;
+ };
+
+ return switch wasi.sock_shutdown(s, sd) {
+ case .Success => .None;
+ case #default => .OperationFailed;
+ };
+}
+
+__net_sock_close :: (s: SocketData) -> void {
+ wasi.fd_close(s);
+}
+
+__net_resolve :: (host: str, port: u16, out_addrs: [] SocketAddress) -> i32 {
+}
+
+
+#package {
+ socket_addr_to_wasix_addr :: (in: &SocketAddress, out: &wasi.AddrPort) {
+ switch *in {
+ case &path: .Unix {
+ *out = .{ Unix = .{ *cast(&[108] u8) path } };
+ }
+
+ case &inet: .Inet {
+ *out = .{ Ipv4 = .{ inet.port, *cast(&wasi.AddrIPv4)&inet.addr } };
+ }
+
+ case &inet: .Inet6 {
+ // Not entirely right...
+ *out = .{ Ipv6 = .{ inet.port, *cast(&wasi.AddrIPv6)&inet.addr } };
+ }
+
+ case .HostPort ---
+ }
+ }
+
+ wasi_addr_to_socket_address :: (in: &wasi.AddrPort, out: &SocketAddress) {
+ switch *in {
+ case .Unspec ---
+
+ case &inet: .Ipv4 {
+ *out = .{ Inet = .{ inet.port, *cast(&u32)&inet.addr } };
+ }
+
+ case &inet: .Ipv6 {
+ *out = .{ Inet6 = .{ inet.port, *cast(&[16] u8)&inet.addr } };
+ }
+
+ case &unix: .Unix {
+ p: [256] u8 = *cast(&[256] u8) &unix.path;
+ *out = .{ Unix = p };
+ }
+ }
+ }
+}
\ No newline at end of file