nativity/lib/std/os.nat
David Gonzalez Martin ff21b7d698 remove assert builtin
2024-02-20 20:12:20 -06:00

450 lines
14 KiB
Plaintext

const std = #import("std");
const Allocator = std.Allocator;
const assert = std.assert;
const current = #import("builtin").os;
const system = switch (current) {
.linux => linux,
.macos => macos,
.windows => windows,
};
const exit = fn(exit_code: s32) noreturn {
switch (current) {
.linux => _ = #syscall(#cast(linux.Syscall.exit_group), #cast(exit_code)),
.macos => macos.exit(exit_code),
.windows => windows.ExitProcess(#cast(exit_code)),
}
}
const max_file_operation_byte_count = switch (current) {
.linux => 0x7ffff000,
.macos => 0x7fffffff,
else => #error("OS not supported"),
};
const unwrap_syscall_signed = fn(syscall_result: ssize) bool {
return syscall_result >= 0;
}
const FileDescriptor = struct{
handle: system.FileDescriptor,
const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ?usize {
if (bytes.len > 0) {
switch (current) {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
if (linux.unwrap_syscall(syscall_result = #syscall(#cast(linux.Syscall.read), #cast(file_descriptor.handle), #cast(bytes.ptr), len))) |byte_count| {
return byte_count;
} else {
return null;
}
},
else => #error("OS not supported"),
}
} else {
return 0;
}
}
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize {
switch (current) {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const raw_result = #syscall(#cast(linux.Syscall.write), #cast(file_descriptor.handle), #cast(bytes.ptr), len);
if (linux.unwrap_syscall(syscall_result = raw_result)) |byte_count| {
return byte_count;
} else {
return null;
}
},
.macos => {
const raw_result = macos.write(file_descriptor.handle, bytes.ptr, bytes.len);
if (unwrap_syscall_signed(raw_result)) {
const bytes_written: usize = #cast(raw_result);
return bytes_written;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
};
const StdFileDescriptor = enum {
stdin = 0,
stdout = 1,
stderr = 2,
const get = fn(descriptor: StdFileDescriptor) FileDescriptor{
switch (current) {
.linux, .macos => {
return FileDescriptor{
.handle = #cast(descriptor),
};
},
else => #error("OS not supported"),
}
}
};
const ProtectionFlags = struct(u32){
read: bool,
write: bool,
execute: bool,
};
const MapFlags = struct(u32){
reserve: bool,
commit: bool,
};
const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_protection_flags: ProtectionFlags, general_map_flags: MapFlags) ?[&]u8 {
const protection_flags = system.get_protection_flags(flags = general_protection_flags);
const map_flags = system.get_map_flags(flags = general_map_flags);
const fd = -1;
const offset = 0;
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.mmap(address, length, protection_flags, map_flags, fd, offset))) |result_address| {
const pointer: [&]u8 = #cast(result_address);
return pointer;
} else {
return null;
}
},
.macos => {
const result = macos.mmap(address, length, protection_flags, map_flags, fd, offset);
if (result != macos.MAP_FAILED) {
const result_address: [&]u8 = #cast(result);
return result_address;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const free_virtual_memory = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.munmap(bytes_ptr, bytes_len))) |result| {
return result == 0;
} else {
return false;
}
},
.macos => {
const raw_result = macos.munmap(bytes_ptr, bytes_len);
const result = unwrap_syscall_signed(raw_result);
return result;
},
else => #error("OS not supported"),
}
}
const readlink = fn(file_path: [&:0]const u8, buffer: []u8) ?[]u8 {
switch (current) {
.linux => {
const raw_result = linux.readlink(file_path, bytes_ptr = buffer.ptr, bytes_len = buffer.len);
if (linux.unwrap_syscall(syscall_result = raw_result)) |byte_count| {
const bytes = buffer[0..byte_count];
return bytes;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const max_path_byte_count = switch (current) {
.linux => 0x1000,
.macos => 1024,
else => #error("OS not supported"),
};
const current_executable_path = fn(buffer: [:0]u8) ?[]u8 {
switch (current) {
.linux => {
if (readlink(file_path = "/proc/self/exe", buffer)) |bytes| {
return bytes;
} else {
return null;
}
},
.macos => {
var symlink_path_buffer: [max_path_byte_count:0]u8 = undefined;
var symlink_path_len: u32 = symlink_path_buffer.len + 1;
const ns_result = macos._NSGetExecutablePath(symlink_path_buffer.&, symlink_path_len.&);
if (ns_result == 0) {
const symlink_path = symlink_path_buffer[0..symlink_path_len];
const result = macos.realpath(symlink_path.ptr, buffer.ptr);
if (result != null) {
var i: usize = 0;
while (i < buffer.len) {
if (result[i] == 0) {
break;
}
i += 1;
}
assert(i < buffer.len);
return result[0..i];
} else {
return null;
}
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const Process = struct{
const Id = system.ProcessId;
};
const duplicate_process = fn () ?Process.Id {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.fork())) |fork_result| {
const unsigned: u32 = #cast(fork_result);
const signed: s32 = #cast(unsigned);
return signed;
} else {
return null;
}
},
.macos => {
const fork_result = macos.fork();
if (unwrap_syscall_signed(fork_result)) {
return fork_result;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) void {
switch (current) {
.linux => {
_ = linux.execve(path, argv, env);
},
.macos => {
_ = macos.execve(path, argv, env);
},
else => #error("OS not supported"),
}
}
const event_file_descriptor = fn(initial_value: u32, flags: u32) ?s32 {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.event_file_descriptor(count = initial_value, flags))) |raw_result| {
const result: s32 = #cast(raw_result);
return result;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) bool {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.dup2(old = old_file_descriptor, new = new_file_descriptor))) |_| {
return true;
} else {
return false;
}
},
else => #error("OS not supported"),
}
}
const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) ?FileDescriptor{
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.open(path, flags, permissions))) |raw_result| {
const file_descriptor = FileDescriptor{
.handle = #cast(raw_result),
};
return file_descriptor;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const close = fn(file_descriptor: s32) bool {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.close(file_descriptor))) |_| {
return true;
} else {
return false;
}
},
else => #error("OS not supported"),
}
}
const pipe2 = fn(flags: u32) ?[2]system.FileDescriptor{
switch (current) {
.linux => {
var pipe: [2]s32 = undefined;
if (linux.unwrap_syscall(syscall_result = linux.pipe2(pipe_pointer = pipe.&, flags))) |_| {
return pipe;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const set_up_child_process_io_posix = fn(io_channel_behavior: IoChannelBehavior, pipe_file_descriptor: s32, std_io_channel_descriptor: s32, dev_null_file_descriptor: s32) bool {
switch (io_channel_behavior) {
.pipe => return dup2(old_file_descriptor = pipe_file_descriptor, new_file_descriptor = std_io_channel_descriptor),
.close => {
if (!close(file_descriptor = std_io_channel_descriptor)) {
unreachable;
}
return true;
},
.inherit => return true,
.ignore => return dup2(old_file_descriptor = dev_null_file_descriptor, new_file_descriptor = std_io_channel_descriptor),
}
}
const PollFileDescriptor = system.PollFileDescriptor;
const poll = fn(file_descriptors: []PollFileDescriptor, timeout: s32) ?usize {
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.poll(file_descriptors = file_descriptors.ptr, file_descriptor_count = file_descriptors.len, timeout = timeout))) |result| {
return result;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const write_u64_pipe = fn (file_handle: s32, value: u64) ?usize {
const file = FileDescriptor{
.handle = file_handle,
};
const value_ptr: [&]u8 = #cast(value.&);
const bytes = value_ptr[0..#size(u64)];
return file.write(bytes);
}
const read_u64_pipe = fn (file_handle: s32) ?u64{
const file = FileDescriptor{
.handle = file_handle,
};
var value: u64 = 0;
const value_ptr: [&]u8 = #cast(value.&);
const bytes = value_ptr[0..#size(u64)];
if (file.read(bytes)) |character_read_count| {
if (character_read_count == #size(u64)) {
return value;
} else {
return null;
}
} else {
return null;
}
}
const termsig = fn(status: u32) u32 {
return status & 0x7f;
}
const ifexited = fn(status: u32) bool {
return termsig(status) == 0;
}
const exitstatus = fn(status: u32) u8 {
const result: u8 = #cast((status & 0xff00) >> 8);
return result;
}
const stopsig = fn(status: u32) u32 {
return exitstatus(status);
}
const ifstopped = fn(status: u32) bool {
const result: u16 = #cast(((status & 0xffff) * 0x10001) >> 8);
return result > 0x7f00;
}
const ifsignaled = fn(status: u32) bool {
return (status & 0xffff) - 1 < 0xff;
}
const waitpid = fn(pid: Process.Id, flags: u32) ?u32 {
switch (current) {
.linux => {
var status: u32 = undefined;
while (true) {
const raw_syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0);
const signed_syscall_result: ssize = #cast(raw_syscall_result);
if (raw_syscall_result != -4) {
if (linux.unwrap_syscall(syscall_result = raw_syscall_result)) |_| {
return status;
} else {
return null;
}
}
}
},
else => #error("OS not supported"),
}
}
const memfd_create = fn(name: [&:0]const u8, flags: u32) ?FileDescriptor{
switch (current) {
.linux => {
if (linux.unwrap_syscall(syscall_result = linux.memfd_create(path, flags))) |raw_result| {
const file_descriptor = FileDescriptor{
.handle = #cast(raw_result),
};
return file_descriptor;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const IoChannelBehavior = enum{
pipe,
close,
inherit,
ignore,
};
const linux = #import("os/linux.nat");
const macos = #import("os/macos.nat");
const windows = #import("os/windows.nat");