+++ /dev/null
-@TODO // Move this file to the core.os package
-package core.io
-
-#local runtime :: package runtime
-#if runtime.Runtime != runtime.Runtime_Wasi
- && runtime.Runtime != runtime.Runtime_Onyx {
- #error "The file system library is currently only available on the WASI runtime, and should only be included if that is the chosen runtime."
-}
-
-use package core
-
-#local wasi :: package wasi
-use package wasi {
- FileDescriptor,
- FDFlags, OFlags, Rights,
- LookupFlags, Errno,
- IOVec, IOVecArray, Size,
- FileStat, Whence
-}
-
-OpenMode :: enum {
- Invalid;
- Read;
- Write;
- Append;
-}
-
-File :: struct {
- fd : FileDescriptor;
-
- mode : OpenMode = .Invalid;
- rights : Rights = ~~ 0;
- flags : FDFlags = ~~ 0;
-}
-
-file_open :: (path: str, mode := OpenMode.Read, flags := FDFlags.Sync) -> (File, bool) {
- // Requesting all of the rights because why not.
- rights :=
- Rights.DataSync
- | Rights.Read
- | Rights.Seek
- | Rights.FdStatSetFlags
- | Rights.Sync
- | Rights.Tell
- | Rights.Write
- | Rights.Advise
- | Rights.Allocate
- | Rights.PathCreateDirectory
- | Rights.PathCreateFile
- | Rights.PathLinkSource
- | Rights.PathLinkTarget
- | Rights.PathOpen
- | Rights.ReadDir
- | Rights.PathReadlink
- | Rights.PathRenameSource
- | Rights.PathRenameTarget
- | Rights.PathFilestatGet
- | Rights.PathFilestateSetSize
- | Rights.PathFilestateSetTimes
- | Rights.FilestatGet
- | Rights.FilestatSetSize
- | Rights.FilestatSetTimes
- | Rights.PathSymlink
- | Rights.PathRemoveDirectory
- | Rights.PathUnlinkFile
- | Rights.PollFDReadWrite;
-
- open_flags := cast(OFlags) 0;
- fd_flags := flags;
-
- switch mode {
- case .Write {
- open_flags |= OFlags.Creat | OFlags.Trunc;
- rights |= Rights.Write | Rights.Seek | Rights.Tell;
- }
-
- case .Append {
- open_flags |= OFlags.Creat;
- rights |= Rights.Write | Rights.Seek | Rights.Tell;
- fd_flags |= FDFlags.Append;
- }
-
- case .Read {
- rights |= Rights.Read | Rights.Seek | Rights.Tell;
- }
- }
-
- file := File.{ fd = -1 };
- file.mode = mode;
- file.rights = rights;
- file.flags = fd_flags;
-
- // Currently the directory's file descriptor appears to always be 3
- // However, this is not necessarily correct, so also try a preopened directory
- for DIR_FD: .[ 3, 4 ] {
- if err := wasi.path_open(
- DIR_FD,
- .SymLinkFollow,
- path,
- open_flags,
- rights,
- rights,
- fd_flags,
- ^file.fd);
- err == .Success {
- return file, true;
- }
- }
-
- return file, false;
-}
-
-file_close :: (file: File) -> bool {
- if wasi.fd_close(file.fd) != .Success {
- return false;
- }
-
- return true;
-}
-
-file_write :: (file: File, data: str) {
- vec := IOVec.{ buf = cast(u32) data.data, len = data.count };
- tmp : Size;
- wasi.fd_write(file.fd, .{ cast(u32) ^vec, 1 }, ^tmp);
- wasi.fd_datasync(file.fd);
-}
-
-file_get_size :: (file: File) -> u64 {
- fs: FileStat;
- if wasi.fd_filestat_get(file.fd, ^fs) != Errno.Success do return 0;
-
- return fs.size;
-}
-
-get_contents_from_file :: (file: File) -> str {
- size := cast(u32) file_get_size(file);
-
- data := cast(^u8) raw_alloc(context.allocator, size);
-
- prev_loc: i64;
- wasi.fd_tell(file.fd, ^prev_loc);
-
- dummy: i64;
- wasi.fd_seek(file.fd, 0, .Set, ^dummy);
-
- dummy2: u32;
- buf := IOVec.{ cast(u32) data, size };
- wasi.fd_pread(file.fd, .{ cast(u32) ^buf, 1 }, 0, ^dummy2);
-
- wasi.fd_seek(file.fd, prev_loc, .Set, ^dummy);
-
- return data[0 .. size];
-}
-
-get_contents :: #match {
- get_contents_from_file,
-
- (path: str) -> str {
- tmp_file, success := file_open(path, .Read);
- if !success do return .{ null, 0 };
- defer file_close(tmp_file);
-
- return get_contents(tmp_file);
- }
-}
-
-file_exists :: (path: str) -> bool {
- fs: wasi.FileStat;
-
- exists := false;
- for .[3, 4] { // Trying both preopened directories
- err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
- if err == .Success do exists = true;
- }
-
- return exists;
-}
-
-FileStream :: struct {
- use stream : Stream;
- use file : File;
-}
-
-open :: (path: str, mode := OpenMode.Read) -> (Error, FileStream) {
- fs := FileStream.{
- stream = .{ vtable = null },
- file = .{ fd = -1 },
- };
-
- file, success := file_open(path, mode);
- if !success do return .NotFound, fs;
-
- fs.file = file;
- fs.vtable = ^wasi_file_stream_vtable;
- return .None, fs;
-}
-
-close :: (file: ^FileStream) {
- file_close(file.file);
- file.stream.vtable = null;
-}
-
-with_file :: (path: str, mode := OpenMode.Read) -> Iterator(^FileStream) {
- Context :: struct {
- valid_file_stream := false;
-
- file_stream: FileStream;
- }
-
- c := new(Context);
- if err, file_stream := open(path, mode); err == .None {
- c.file_stream = file_stream;
- c.valid_file_stream = true;
- }
-
- next :: (use c: ^Context) -> (^FileStream, bool) {
- if !valid_file_stream do return null, false;
-
- defer valid_file_stream = false;
- return ^file_stream, true;
- }
-
- close_context :: (use c: ^Context) {
- close(^file_stream);
- cfree(c);
- }
-
- return .{ c, next, close_context };
-}
-
-#package
-wasi_file_stream_vtable := Stream_Vtable.{
- seek = (use fs: ^FileStream, to: i32, whence: SeekFrom) -> Error {
- // Currently, the new offset is just ignored.
- newoffset : wasi.Filesize;
- error := wasi.fd_seek(file.fd, ~~ to, ~~ whence, ^newoffset);
- if error != .Success do return .BadFile;
-
- return Error.None;
- },
-
- tell = (use fs: ^FileStream) -> (Error, u32) {
- location : wasi.Filesize;
- error := wasi.fd_tell(file.fd, ^location);
- if error != .Success do return .BadFile, 0;
-
- return .None, ~~location;
- },
-
- read = (use fs: ^FileStream, buffer: [] u8) -> (Error, u32) {
- bytes_read : wasi.Size;
- vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
- error := wasi.fd_read(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_read);
- if error != .Success do return .BadFile, 0;
-
- return .None, bytes_read;
- },
-
- read_at = (use fs: ^FileStream, at: u32, buffer: [] u8) -> (Error, u32) {
- bytes_read : wasi.Size;
- vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
- error := wasi.fd_pread(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ~~at, ^bytes_read);
-
- // FIX: Maybe report Error.OutOfBounds if the 'at' was out of bounds?
- if error != .Success do return .BadFile, 0;
-
- return .None, bytes_read;
- },
-
- read_byte = (use fs: ^FileStream) -> (Error, u8) {
- bytes_read : wasi.Size;
- byte : u8;
- vec := IOVec.{ buf = cast(u32) ^byte, len = 1};
- error := wasi.fd_read(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_read);
- if error != .Success do return .BadFile, 0;
-
- return .None, byte;
- },
-
- write = (use fs: ^FileStream, buffer: [] u8) -> (Error, u32) {
- bytes_written : wasi.Size;
- vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
- error := wasi.fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_written);
- if error != .Success do return .BadFile, 0;
-
- return .None, bytes_written;
- },
-
- write_at = (use fs: ^FileStream, at: u32, buffer: [] u8) -> (Error, u32) {
- bytes_written : wasi.Size;
- vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
- error := wasi.fd_pwrite(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ~~at, ^bytes_written);
-
- // FIX: Maybe report Error.OutOfBounds if the 'at' was out of bounds?
- if error != .Success do return .BadFile, 0;
-
- return .None, bytes_written;
- },
-
- write_byte = (use fs: ^FileStream, byte: u8) -> Error {
- bytes_written : wasi.Size;
- byte_to_write := byte;
- vec := IOVec.{ buf = cast(u32) ^byte_to_write, len = 1 };
- error := wasi.fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_written);
- if error != .Success do return .BadFile;
-
- return .None;
- },
-
- close = (use fs: ^FileStream) -> Error {
- file_close(file);
- return .None;
- },
-
- flush = (use fs: ^FileStream) -> Error {
- wasi.fd_datasync(file.fd);
- return .None;
- },
-
- size = (use fs: ^FileStream) -> i32 {
- file_stat: FileStat;
- if wasi.fd_filestat_get(file.fd, ^file_stat) != .Success do return 0;
-
- return ~~ file_stat.size;
- },
-}
-
-
-
-
-file_logger_open :: (filename: str, allocator := context.allocator) -> Logger {
- file := new(File, allocator);
- success := false;
-
- *file, success = file_open(filename, mode=.Append);
- assert(success, "Unable to open file for logging.");
-
- return .{ file_logger_proc, file };
-}
-
-file_logger_close :: (logger := context.logger) {
- file_close(*cast(^File) logger.data);
-
- // @Robustness: this could be the wrong allocator if the context allocator wasn't used.
- cfree(logger.data);
-}
-
-#local
-file_logger_proc :: (data: ^File, msg: str) {
- file_write(*data, msg);
- file_write(*data, "\n");
-}
--- /dev/null
+package core.os
+
+#local runtime :: package runtime
+#if runtime.Runtime != runtime.Runtime_Wasi
+ && runtime.Runtime != runtime.Runtime_Onyx {
+ #error "The file system library is currently only available on the WASI runtime, and should only be included if that is the chosen runtime."
+}
+
+use package core
+
+#local wasi :: package wasi
+use package wasi {
+ FileDescriptor,
+ FDFlags, OFlags, Rights,
+ LookupFlags, Errno,
+ IOVec, IOVecArray, Size,
+ FileStat, Whence
+}
+
+OpenMode :: enum {
+ Invalid;
+ Read;
+ Write;
+ Append;
+}
+
+File :: struct {
+ fd : FileDescriptor;
+
+ mode : OpenMode = .Invalid;
+ rights : Rights = ~~ 0;
+ flags : FDFlags = ~~ 0;
+}
+
+file_open :: (path: str, mode := OpenMode.Read, flags := FDFlags.Sync) -> (File, bool) {
+ // Requesting all of the rights because why not.
+ rights :=
+ Rights.DataSync
+ | Rights.Read
+ | Rights.Seek
+ | Rights.FdStatSetFlags
+ | Rights.Sync
+ | Rights.Tell
+ | Rights.Write
+ | Rights.Advise
+ | Rights.Allocate
+ | Rights.PathCreateDirectory
+ | Rights.PathCreateFile
+ | Rights.PathLinkSource
+ | Rights.PathLinkTarget
+ | Rights.PathOpen
+ | Rights.ReadDir
+ | Rights.PathReadlink
+ | Rights.PathRenameSource
+ | Rights.PathRenameTarget
+ | Rights.PathFilestatGet
+ | Rights.PathFilestateSetSize
+ | Rights.PathFilestateSetTimes
+ | Rights.FilestatGet
+ | Rights.FilestatSetSize
+ | Rights.FilestatSetTimes
+ | Rights.PathSymlink
+ | Rights.PathRemoveDirectory
+ | Rights.PathUnlinkFile
+ | Rights.PollFDReadWrite;
+
+ open_flags := cast(OFlags) 0;
+ fd_flags := flags;
+
+ switch mode {
+ case .Write {
+ open_flags |= OFlags.Creat | OFlags.Trunc;
+ rights |= Rights.Write | Rights.Seek | Rights.Tell;
+ }
+
+ case .Append {
+ open_flags |= OFlags.Creat;
+ rights |= Rights.Write | Rights.Seek | Rights.Tell;
+ fd_flags |= FDFlags.Append;
+ }
+
+ case .Read {
+ rights |= Rights.Read | Rights.Seek | Rights.Tell;
+ }
+ }
+
+ file := File.{ fd = -1 };
+ file.mode = mode;
+ file.rights = rights;
+ file.flags = fd_flags;
+
+ // Currently the directory's file descriptor appears to always be 3
+ // However, this is not necessarily correct, so also try a preopened directory
+ for DIR_FD: .[ 3, 4 ] {
+ if err := wasi.path_open(
+ DIR_FD,
+ .SymLinkFollow,
+ path,
+ open_flags,
+ rights,
+ rights,
+ fd_flags,
+ ^file.fd);
+ err == .Success {
+ return file, true;
+ }
+ }
+
+ return file, false;
+}
+
+file_close :: (file: File) -> bool {
+ if wasi.fd_close(file.fd) != .Success {
+ return false;
+ }
+
+ return true;
+}
+
+file_write :: (file: File, data: str) {
+ vec := IOVec.{ buf = cast(u32) data.data, len = data.count };
+ tmp : Size;
+ wasi.fd_write(file.fd, .{ cast(u32) ^vec, 1 }, ^tmp);
+ wasi.fd_datasync(file.fd);
+}
+
+file_get_size :: (file: File) -> u64 {
+ fs: FileStat;
+ if wasi.fd_filestat_get(file.fd, ^fs) != Errno.Success do return 0;
+
+ return fs.size;
+}
+
+get_contents_from_file :: (file: File) -> str {
+ size := cast(u32) file_get_size(file);
+
+ data := cast(^u8) raw_alloc(context.allocator, size);
+
+ prev_loc: i64;
+ wasi.fd_tell(file.fd, ^prev_loc);
+
+ dummy: i64;
+ wasi.fd_seek(file.fd, 0, .Set, ^dummy);
+
+ dummy2: u32;
+ buf := IOVec.{ cast(u32) data, size };
+ wasi.fd_pread(file.fd, .{ cast(u32) ^buf, 1 }, 0, ^dummy2);
+
+ wasi.fd_seek(file.fd, prev_loc, .Set, ^dummy);
+
+ return data[0 .. size];
+}
+
+get_contents :: #match {
+ get_contents_from_file,
+
+ (path: str) -> str {
+ tmp_file, success := file_open(path, .Read);
+ if !success do return .{ null, 0 };
+ defer file_close(tmp_file);
+
+ return get_contents(tmp_file);
+ }
+}
+
+file_exists :: (path: str) -> bool {
+ fs: wasi.FileStat;
+
+ exists := false;
+ for .[3, 4] { // Trying both preopened directories
+ err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
+ if err == .Success do exists = true;
+ }
+
+ return exists;
+}
+
+FileStream :: struct {
+ use stream : io.Stream;
+ use file : File;
+}
+
+open :: (path: str, mode := OpenMode.Read) -> (io.Error, FileStream) {
+ fs := FileStream.{
+ stream = .{ vtable = null },
+ file = .{ fd = -1 },
+ };
+
+ file, success := file_open(path, mode);
+ if !success do return .NotFound, fs;
+
+ fs.file = file;
+ fs.vtable = ^wasi_file_stream_vtable;
+ return .None, fs;
+}
+
+close :: (file: ^FileStream) {
+ file_close(file.file);
+ file.stream.vtable = null;
+}
+
+with_file :: (path: str, mode := OpenMode.Read) -> Iterator(^FileStream) {
+ Context :: struct {
+ valid_file_stream := false;
+
+ file_stream: FileStream;
+ }
+
+ c := new(Context);
+ if err, file_stream := open(path, mode); err == .None {
+ c.file_stream = file_stream;
+ c.valid_file_stream = true;
+ }
+
+ next :: (use c: ^Context) -> (^FileStream, bool) {
+ if !valid_file_stream do return null, false;
+
+ defer valid_file_stream = false;
+ return ^file_stream, true;
+ }
+
+ close_context :: (use c: ^Context) {
+ close(^file_stream);
+ cfree(c);
+ }
+
+ return .{ c, next, close_context };
+}
+
+#package
+wasi_file_stream_vtable := io.Stream_Vtable.{
+ seek = (use fs: ^FileStream, to: i32, whence: io.SeekFrom) -> io.Error {
+ // Currently, the new offset is just ignored.
+ newoffset : wasi.Filesize;
+ error := wasi.fd_seek(file.fd, ~~ to, ~~ whence, ^newoffset);
+ if error != .Success do return .BadFile;
+
+ return .None;
+ },
+
+ tell = (use fs: ^FileStream) -> (io.Error, u32) {
+ location : wasi.Filesize;
+ error := wasi.fd_tell(file.fd, ^location);
+ if error != .Success do return .BadFile, 0;
+
+ return .None, ~~location;
+ },
+
+ read = (use fs: ^FileStream, buffer: [] u8) -> (io.Error, u32) {
+ bytes_read : wasi.Size;
+ vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+ error := wasi.fd_read(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_read);
+ if error != .Success do return .BadFile, 0;
+
+ return .None, bytes_read;
+ },
+
+ read_at = (use fs: ^FileStream, at: u32, buffer: [] u8) -> (io.Error, u32) {
+ bytes_read : wasi.Size;
+ vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+ error := wasi.fd_pread(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ~~at, ^bytes_read);
+
+ // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
+ if error != .Success do return .BadFile, 0;
+
+ return .None, bytes_read;
+ },
+
+ read_byte = (use fs: ^FileStream) -> (io.Error, u8) {
+ bytes_read : wasi.Size;
+ byte : u8;
+ vec := IOVec.{ buf = cast(u32) ^byte, len = 1};
+ error := wasi.fd_read(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_read);
+ if error != .Success do return .BadFile, 0;
+
+ return .None, byte;
+ },
+
+ write = (use fs: ^FileStream, buffer: [] u8) -> (io.Error, u32) {
+ bytes_written : wasi.Size;
+ vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+ error := wasi.fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_written);
+ if error != .Success do return .BadFile, 0;
+
+ return .None, bytes_written;
+ },
+
+ write_at = (use fs: ^FileStream, at: u32, buffer: [] u8) -> (io.Error, u32) {
+ bytes_written : wasi.Size;
+ vec := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+ error := wasi.fd_pwrite(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ~~at, ^bytes_written);
+
+ // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
+ if error != .Success do return .BadFile, 0;
+
+ return .None, bytes_written;
+ },
+
+ write_byte = (use fs: ^FileStream, byte: u8) -> io.Error {
+ bytes_written : wasi.Size;
+ byte_to_write := byte;
+ vec := IOVec.{ buf = cast(u32) ^byte_to_write, len = 1 };
+ error := wasi.fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^bytes_written);
+ if error != .Success do return .BadFile;
+
+ return .None;
+ },
+
+ close = (use fs: ^FileStream) -> io.Error {
+ file_close(file);
+ return .None;
+ },
+
+ flush = (use fs: ^FileStream) -> io.Error {
+ wasi.fd_datasync(file.fd);
+ return .None;
+ },
+
+ size = (use fs: ^FileStream) -> i32 {
+ file_stat: FileStat;
+ if wasi.fd_filestat_get(file.fd, ^file_stat) != .Success do return 0;
+
+ return ~~ file_stat.size;
+ },
+}
+
+
+
+
+file_logger_open :: (filename: str, allocator := context.allocator) -> Logger {
+ file := new(File, allocator);
+ success := false;
+
+ *file, success = file_open(filename, mode=.Append);
+ assert(success, "Unable to open file for logging.");
+
+ return .{ file_logger_proc, file };
+}
+
+file_logger_close :: (logger := context.logger) {
+ file_close(*cast(^File) logger.data);
+
+ // @Robustness: this could be the wrong allocator if the context allocator wasn't used.
+ cfree(logger.data);
+}
+
+#local
+file_logger_proc :: (data: ^File, msg: str) {
+ file_write(*data, msg);
+ file_write(*data, "\n");
+}
#load "./wasi/wasi"
#load "./wasi/env"
#load "./wasi/clock"
- #load "./io/file"
+ #load "./os/file"
#load "./os/os"
}
test_case := string.concat(path_buffer, root, "/", it.name) |> string.alloc_copy();
expected_file := test_case[0 .. (test_case.count - 5)];
- if !io.file_exists(expected_file) {
+ if !os.file_exists(expected_file) {
print_color(.Yellow, "Skipping test case {} because an expected output file was not found.\n", test_case);
continue;
}
continue;
}
- for expected_file: io.with_file(it.expected_file) {
+ for expected_file: os.with_file(it.expected_file) {
expected_reader := io.reader_make(expected_file);
expected_output := io.read_all(^expected_reader);
}
main :: (args) => {
- #if false {
- contents := #file_contents "./input/day01.txt";
- reader, stream := io.reader_from_string(contents);
- defer cfree(stream);
- }
-
- err, file := io.open("./tests/aoc-2021/input/day01.txt");
+ err, file := os.open("./tests/aoc-2021/input/day01.txt");
assert(err == .None, "Error opening file");
reader := io.reader_make(^file);
- defer io.close(^file);
+ defer os.close(^file);
nums := array.make(i32);
while !io.reader_empty(^reader) {
use package core
main :: (args) => {
- for file: io.with_file("tests/aoc-2021/input/day02.txt") {
+ for file: os.with_file("tests/aoc-2021/input/day02.txt") {
reader := io.reader_make(file);
#if PART == 1 {
main :: (args) => {
BITS :: 12
- for io.with_file("./tests/aoc-2021/input/day03.txt") {
+ for os.with_file("./tests/aoc-2021/input/day03.txt") {
reader := io.reader_make(it);
nums := array.make(i32);