ona/src/coral/files.zig
2024-06-23 02:16:04 +01:00

125 lines
2.9 KiB
Zig

const builtin = @import("builtin");
const io = @import("./io.zig");
const std = @import("std");
pub const Error = error {
FileNotFound,
FileInaccessible,
};
pub const Stat = struct {
size: u64,
};
pub const Storage = struct {
userdata: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
stat: *const fn (*anyopaque, []const u8) Error!Stat,
read: *const fn (*anyopaque, []const u8, usize, []io.Byte) Error!usize,
};
pub fn read_bytes(self: Storage, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
return self.vtable.read(self.userdata, path, offset, output);
}
pub fn read_foreign(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
const decoded = (try self.read_native(path, offset, Type)) orelse {
return null;
};
return switch (@typeInfo(Type)) {
.Struct => std.mem.byteSwapAllFields(Type, &decoded),
else => @byteSwap(decoded),
};
}
pub fn read_native(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
var buffer = @as([@sizeOf(Type)]io.Byte, undefined);
if (try self.vtable.read(self.userdata, path, offset, &buffer) != buffer.len) {
return null;
}
return @as(*align(1) const Type, @ptrCast(&buffer)).*;
}
pub const read_little = switch (native_endian) {
.little => read_native,
.big => read_foreign,
};
pub const read_big = switch (native_endian) {
.little => read_foreign,
.big => read_native,
};
};
pub const bundle = init: {
const Bundle = struct {
fn full_path(path: []const u8) Error![4095:0]u8 {
var buffer = [_:0]u8{0} ** 4095;
_ = std.fs.cwd().realpath(path, &buffer) catch {
return error.FileInaccessible;
};
return buffer;
}
fn read(_: *anyopaque, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
return switch (open_error) {
error.FileNotFound => error.FileNotFound,
else => error.FileInaccessible,
};
};
defer file.close();
if (offset != 0) {
file.seekTo(offset) catch {
return error.FileInaccessible;
};
}
return file.read(output) catch error.FileInaccessible;
}
fn stat(_: *anyopaque, path: []const u8) Error!Stat {
const file_stat = get: {
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
return switch (open_error) {
error.FileNotFound => error.FileNotFound,
else => error.FileInaccessible,
};
};
defer file.close();
break: get file.stat() catch {
return error.FileInaccessible;
};
};
return .{
.size = file_stat.size,
};
}
};
break: init Storage{
.userdata = undefined,
.vtable = &.{
.stat = Bundle.stat,
.read = Bundle.read,
},
};
};
const native_endian = builtin.cpu.arch.endian();