125 lines
2.9 KiB
Zig
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();
|