-package core.file
-
-// Many of these functions will be improved when
-// multiple return values are implemented.
-
+package core.io
use package core
-use package wasi
+use package wasi as 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;
+ fd : FileDescriptor;
+
+ mode : OpenMode = OpenMode.Invalid;
+ rights : Rights = ~~ 0;
+ flags : FDFlags = ~~ 0;
}
-open :: proc (file: ^File, path: str, mode := OpenMode.Read, flags := FDFlags.Sync) -> bool {
+file_open :: (path: str, mode := OpenMode.Read, flags := FDFlags.Sync) -> (File, bool) {
// Currently the directory's file descriptor appears to always be 3
DIR_FD :: 3;
- open_flags := cast(OFlags) 0;
- // rights := Rights.DataSync
- // | Rights.Sync
- // | Rights.FilestatGet
- // | Rights.FilestatSetSize
- // | Rights.FilestatSetTimes
- // | Rights.Advise
- // | Rights.Allocate
- // | Rights.PathOpen
- // | Rights.PathCreateFile;
-
+ // Requesting all of the rights because why not.
rights :=
Rights.DataSync
| Rights.Read
| Rights.PathUnlinkFile
| Rights.PollFDReadWrite;
- fdflags := flags;
+ open_flags := cast(OFlags) 0;
+ fd_flags := flags;
switch mode {
case OpenMode.Write {
case OpenMode.Append {
open_flags |= OFlags.Creat;
rights |= Rights.Write;
- fdflags |= FDFlags.Append;
+ fd_flags |= FDFlags.Append;
}
case OpenMode.Read {
}
}
- if err := path_open(
+ file := File.{ fd = -1 };
+
+ if err := wasi.path_open(
DIR_FD,
LookupFlags.SymLinkFollow,
path,
open_flags,
rights,
rights,
- fdflags,
+ fd_flags,
^file.fd);
err != Errno.Success {
- return false;
+ return file, false;
}
- return true;
+ file.mode = mode;
+ file.rights = rights;
+ file.flags = fd_flags;
+ return file, true;
}
-close :: proc (file: File) -> bool {
- if fd_close(file.fd) != Errno.Success {
+file_close :: (file: File) -> bool {
+ if wasi.fd_close(file.fd) != Errno.Success {
return false;
}
return true;
}
-write :: proc (file: File, data: str) {
+file_write :: (file: File, data: str) {
vec := IOVec.{ buf = cast(u32) data.data, len = data.count };
tmp : Size;
- fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^tmp);
- fd_datasync(file.fd);
+ wasi.fd_write(file.fd, IOVecArray.{ cast(u32) ^vec, 1 }, ^tmp);
+ wasi.fd_datasync(file.fd);
}
-get_size :: proc (file: File) -> u64 {
+get_size :: (file: File) -> u64 {
fs: FileStat;
- if fd_filestat_get(file.fd, ^fs) != Errno.Success do return 0;
+ if wasi.fd_filestat_get(file.fd, ^fs) != Errno.Success do return 0;
return fs.size;
}
-get_contents_from_file :: proc (file: File) -> str {
+get_contents_from_file :: (file: File) -> str {
size := cast(u32) get_size(file);
data := cast(^u8) raw_alloc(context.allocator, size);
prev_loc: i64;
- fd_tell(file.fd, ^prev_loc);
+ wasi.fd_tell(file.fd, ^prev_loc);
dummy: i64;
- fd_seek(file.fd, 0, Whence.Set, ^dummy);
+ wasi.fd_seek(file.fd, 0, Whence.Set, ^dummy);
dummy2: u32;
buf := IOVec.{ cast(u32) data, size };
- fd_pread(file.fd, IOVecArray.{ cast(u32) ^buf, 1 }, 0, ^dummy2);
+ wasi.fd_pread(file.fd, IOVecArray.{ cast(u32) ^buf, 1 }, 0, ^dummy2);
- fd_seek(file.fd, prev_loc, Whence.Set, ^dummy);
+ wasi.fd_seek(file.fd, prev_loc, Whence.Set, ^dummy);
return data[0 .. size];
}
get_contents_from_file,
proc (path: str) -> str {
- tmp_file: File;
-
- if !open(^tmp_file, path, OpenMode.Read) do return str.{ null, 0 };
- defer close(tmp_file);
+ tmp_file, success := file_open(path, OpenMode.Read);
+ if !success do return str.{ null, 0 };
+ defer file_close(tmp_file);
return get_contents(tmp_file);
}
}
+
+FileStream :: struct {
+ use stream : Stream;
+ use file : File;
+}
+
+open :: (path: str, mode := OpenMode.Read) -> (Error, FileStream) {
+ fs := FileStream.{
+ stream = Stream.{ vtable = null },
+ file = File.{ fd = -1 },
+ };
+
+ file, success := file_open(path, mode);
+ if !success do return Error.NotFound, fs;
+
+ fs.file = file;
+ fs.vtable = ^file_stream_vtable;
+ return Error.None, fs;
+}
+
+#private
+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 != Errno.Success do return Error.BadFile;
+
+ return Error.None;
+ },
+
+ tell = (use fs: ^FileStream) -> (Error, u32) {
+ location : wasi.Filesize;
+ error := wasi.fd_tell(file.fd, ^location);
+ if error != Errno.Success do return Error.BadFile, 0;
+
+ return Error.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 != Errno.Success do return Error.BadFile, 0;
+
+ return Error.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 != Errno.Success do return Error.BadFile, 0;
+
+ return Error.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 != Errno.Success do return Error.BadFile, 0;
+
+ return Error.None, byte;
+ },
+
+ unread_byte = (use fs: ^FileStream) -> Error {
+ return Error.NotImplemented;
+ },
+
+ 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 != Errno.Success do return Error.BadFile, 0;
+
+ return Error.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 != Errno.Success do return Error.BadFile, 0;
+
+ return Error.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 != Errno.Success do return Error.BadFile;
+
+ return Error.None;
+ },
+
+ close = (use fs: ^FileStream) -> Error {
+ file_close(file);
+ return Error.None;
+ },
+
+ flush = (use fs: ^FileStream) -> Error {
+ wasi.fd_datasync(file.fd);
+ return Error.None;
+ },
+
+ size = (use fs: ^FileStream) -> i32 {
+ file_stat: FileStat;
+ if wasi.fd_filestat_get(file.fd, ^file_stat) != Errno.Success do return 0;
+
+ return ~~ file_stat.size;
+ },
+}
#private
Stream_Vtable :: struct {
seek : proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error;
- tell : proc (s: ^Stream, out: ^i32) -> Error;
+ tell : proc (s: ^Stream) -> (Error, u32);
read : proc (s: ^Stream, buffer: [] u8) -> (Error, u32);
read_at : proc (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32);
End :: 0x02;
}
-stream_seek :: proc (use s: ^Stream, to: i32, whence: SeekFrom) -> Error {
+stream_seek :: (use s: ^Stream, to: i32, whence: SeekFrom) -> Error {
if vtable == null do return Error.NoVtable;
if vtable.seek == null_proc do return Error.NotImplemented;
return vtable.seek(s, to, whence);
}
-stream_tell :: proc (use s: ^Stream, out: ^i32) -> Error {
- if vtable == null do return Error.NoVtable;
- if vtable.tell == null_proc do return Error.NotImplemented;
+stream_tell :: (use s: ^Stream) -> (Error, u32) {
+ if vtable == null do return Error.NoVtable, 0;
+ if vtable.tell == null_proc do return Error.NotImplemented, 0;
- return vtable.tell(s, out);
+ return vtable.tell(s);
}
-stream_read :: proc (use s: ^Stream, buffer: [] u8) -> (Error, u32) {
+stream_read :: (use s: ^Stream, buffer: [] u8) -> (Error, u32) {
if vtable == null do return Error.NoVtable, 0;
if vtable.read == null_proc do return Error.NotImplemented, 0;
return vtable.read(s, buffer);
}
-stream_read_at :: proc (use s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
+stream_read_at :: (use s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
if vtable == null do return Error.NoVtable, 0;
if vtable.read_at == null_proc do return Error.NotImplemented, 0;
return vtable.read_at(s, at, buffer);
}
-stream_read_byte :: proc (use s: ^Stream) -> (Error, u8) {
+stream_read_byte :: (use s: ^Stream) -> (Error, u8) {
if vtable == null do return Error.NoVtable, cast(u8) 0;
if vtable.read_byte == null_proc do return Error.NotImplemented, 0;
return vtable.read_byte(s);
}
-stream_unread_byte :: proc (use s: ^Stream) -> Error {
+stream_unread_byte :: (use s: ^Stream) -> Error {
if vtable == null do return Error.NoVtable;
if vtable.unread_byte == null_proc do return Error.NotImplemented;
return vtable.unread_byte(s);
}
-stream_write :: proc (use s: ^Stream, buffer: [] u8) -> (Error, u32) {
+stream_write :: (use s: ^Stream, buffer: [] u8) -> (Error, u32) {
if vtable == null do return Error.NoVtable, 0;
if vtable.write == null_proc do return Error.NotImplemented, 0;
return vtable.write(s, buffer);
}
-stream_write_at :: proc (use s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
+stream_write_at :: (use s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
if vtable == null do return Error.NoVtable, 0;
if vtable.write_at == null_proc do return Error.NotImplemented, 0;
return vtable.write_at(s, at, buffer);
}
-stream_write_byte :: proc (use s: ^Stream, byte: u8) -> Error {
+stream_write_byte :: (use s: ^Stream, byte: u8) -> Error {
if vtable == null do return Error.NoVtable;
if vtable.write_byte == null_proc do return Error.NotImplemented;
return vtable.write_byte(s, byte);
}
-stream_close :: proc (use s: ^Stream) -> Error {
+stream_close :: (use s: ^Stream) -> Error {
if vtable == null do return Error.NoVtable;
if vtable.close == null_proc do return Error.NotImplemented;
return vtable.close(s);
}
-stream_flush :: proc (use s: ^Stream) -> Error {
+stream_flush :: (use s: ^Stream) -> Error {
if vtable == null do return Error.NoVtable;
if vtable.flush == null_proc do return Error.NotImplemented;
return vtable.flush(s);
}
-stream_size :: proc (use s: ^Stream) -> i32 {
+stream_size :: (use s: ^Stream) -> i32 {
if vtable == null do return 0;
if vtable.size == null_proc do return 0;
return vtable.size(s);
}
-stream_peek_byte :: proc (use s: ^Stream) -> (Error, u8) {
+stream_peek_byte :: (use s: ^Stream) -> (Error, u8) {
err, out := stream_read_byte(s);
if err != Error.None do return err, 0;
data : str;
}
-string_stream_make :: proc (s: str) -> StringStream {
+string_stream_make :: (s: str) -> StringStream {
return StringStream.{
stream = Stream.{
vtable = ^string_stream_vtable,
#private
string_stream_vtable := Stream_Vtable.{
- seek = proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error {
- ss : ^StringStream = ~~s;
- use ss;
-
+ seek = (use ss: ^StringStream, to: i32, whence: SeekFrom) -> Error {
if to >= data.count do return Error.OutOfBounds;
switch whence {
return Error.None;
},
- tell = proc (s: ^Stream, out: ^i32) -> Error {
- ss : ^StringStream = ~~s;
- use ss;
-
- if out != null do *out = curr_pos;
- return Error.None;
+ tell = (use ss: ^StringStream) -> (Error, u32) {
+ return Error.None, curr_pos;
},
- read = proc (s: ^Stream, buffer: [] u8) -> (Error, u32) {
- ss : ^StringStream = ~~s;
- use ss;
-
+ read = (use ss: ^StringStream, buffer: [] u8) -> (Error, u32) {
if curr_pos >= data.count do return Error.EOF, 0;
bytes_to_read := math.min(buffer.count, data.count - curr_pos);
return Error.None, bytes_to_read;
},
- read_at = proc (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
- ss : ^StringStream = ~~s;
- use ss;
-
+ read_at = (use ss: ^StringStream, at: u32, buffer: [] u8) -> (Error, u32) {
if at >= data.count do return Error.EOF, 0;
bytes_to_read := math.min(buffer.count, data.count - at);
return Error.None, bytes_to_read;
},
- read_byte = proc (s: ^Stream) -> (Error, u8) {
- ss : ^StringStream = ~~s;
- use ss;
-
+ read_byte = (use ss: ^StringStream) -> (Error, u8) {
if curr_pos >= data.count do return Error.EOF, 0;
defer curr_pos += 1;
return Error.None, data[curr_pos];
},
- unread_byte = proc (s: ^Stream) -> Error {
- ss : ^StringStream = ~~s;
- use ss;
-
+ unread_byte = (use ss: ^StringStream) -> Error {
if curr_pos <= 0 do return Error.OutOfBounds;
curr_pos -= 1;
return Error.None;
},
- write = proc (s: ^Stream, buffer: [] u8) -> (Error, u32) {
- ss : ^StringStream = ~~s;
- use ss;
-
+ write = (use ss: ^StringStream, buffer: [] u8) -> (Error, u32) {
if curr_pos >= data.count do return Error.EOF, 0;
bytes_to_write := math.min(buffer.count, data.count - curr_pos);
return Error.None, bytes_to_write;
},
- write_at = proc (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
- ss : ^StringStream = ~~s;
- use ss;
-
+ write_at = (use ss: ^StringStream, at: u32, buffer: [] u8) -> (Error, u32) {
if at >= data.count do return Error.EOF, 0;
bytes_to_write := math.min(buffer.count, data.count - at);
return Error.None, bytes_to_write;
},
- write_byte = proc (s: ^Stream, byte: u8) -> Error {
- ss : ^StringStream = ~~s;
- use ss;
-
+ write_byte = (use ss: ^StringStream, byte: u8) -> Error {
if curr_pos >= data.count do return Error.EOF;
data[curr_pos] = byte;
return Error.None;
},
- size = proc (s: ^Stream) -> i32 {
- ss : ^StringStream = ~~s;
- use ss;
-
+ size = (use ss: ^StringStream) -> i32 {
return data.count;
},
alloc : Allocator;
}
-dynamic_string_stream_make :: proc (init_size := 128, a := context.allocator) -> DynamicStringStream {
+dynamic_string_stream_make :: (init_size := 128, a := context.allocator) -> DynamicStringStream {
data : [..] u8;
array.init(^data, init_size);
};
}
-dynamic_string_stream_to_str :: proc (use dds: ^DynamicStringStream) -> str {
+dynamic_string_stream_to_str :: (use dds: ^DynamicStringStream) -> str {
return data.data[0 .. curr_pos];
}
#private
dynamic_string_stream_vtable := Stream_Vtable.{
- seek = proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ seek = (use dss: ^DynamicStringStream, to: i32, whence: SeekFrom) -> Error {
dest : i32;
switch whence {
case SeekFrom.Start do dest = to;
return Error.None;
},
- tell = proc (s: ^Stream, out: ^i32) -> Error {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
- if out != null do *out = curr_pos;
- return Error.None;
+ tell = (use dss: ^DynamicStringStream) -> (Error, u32) {
+ return Error.None, curr_pos;
},
- read = proc (s: ^Stream, buffer: [] u8) -> (Error, u32) {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ read = (use dss: ^DynamicStringStream, buffer: [] u8) -> (Error, u32) {
if curr_pos >= data.count do return Error.EOF, 0;
bytes_to_read := math.min(buffer.count, data.count - curr_pos);
return Error.None, bytes_to_read;
},
- read_at = proc (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ read_at = (use dss: ^DynamicStringStream, at: u32, buffer: [] u8) -> (Error, u32) {
if at >= data.count do return Error.EOF, 0;
bytes_to_read := math.min(buffer.count, data.count - at);
return Error.None, bytes_to_read;
},
- read_byte = proc (s: ^Stream) -> (Error, u8) {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ read_byte = (use dss: ^DynamicStringStream) -> (Error, u8) {
if curr_pos >= data.count do return Error.EOF, 0;
defer curr_pos += 1;
return Error.None, data[curr_pos];
},
- unread_byte = proc (s: ^Stream) -> Error {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ unread_byte = (use dss: ^DynamicStringStream) -> Error {
if curr_pos <= 0 do return Error.OutOfBounds;
curr_pos -= 1;
return Error.None;
},
- write = proc (s: ^Stream, buffer: [] u8) -> (Error, u32) {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ write = (use dss: ^DynamicStringStream, buffer: [] u8) -> (Error, u32) {
if curr_pos + buffer.count >= data.capacity {
#context_scope {
context.allocator = alloc;
return Error.None, buffer.count;
},
- write_at = proc (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32) {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ write_at = (use dss: ^DynamicStringStream, at: u32, buffer: [] u8) -> (Error, u32) {
if at + buffer.count >= data.capacity {
#context_scope {
context.allocator = alloc;
return Error.None, buffer.count;
},
- write_byte = proc (s: ^Stream, byte: u8) -> Error {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ write_byte = (use dss: ^DynamicStringStream, byte: u8) -> Error {
#context_scope {
context.allocator = alloc;
if !array.ensure_capacity(^data, data.count + 1) do return Error.EOF;
return Error.None;
},
- size = proc (s: ^Stream) -> i32 {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ size = (use dss: ^DynamicStringStream) -> i32 {
return data.count;
},
- flush = proc (s: ^Stream) -> Error {
- dss : ^DynamicStringStream = ~~s;
- use dss;
-
+ flush = (use dss: ^DynamicStringStream) -> Error {
curr_pos = 0;
array.clear(^data);