Initial work merging Oar into Ona itself
This commit is contained in:
parent
32b18e4ebf
commit
7599ce61f2
|
@ -204,6 +204,38 @@ test "Check memory begins with" {
|
|||
try testing.expect(!begins(u8, &.{69, 89, 42, 0}, bytes_sequence));
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a sliced reference of the raw bytes in `pointer`.
|
||||
///
|
||||
/// **Note** that passing a slice will convert it to a byte slice.
|
||||
///
|
||||
pub fn bytes(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) {
|
||||
.Pointer => |info| if (info.is_const) []const u8 else []u8,
|
||||
else => @compileError("`pointer` must be a pointer type"),
|
||||
} {
|
||||
const Pointer = @TypeOf(pointer);
|
||||
const pointer_info = @typeInfo(Pointer).Pointer;
|
||||
|
||||
switch (pointer_info.size) {
|
||||
.Many => @compileError("`pointer` cannot be an unbound pointer type"),
|
||||
.C => @compileError("`pointer` cannot be a C-style pointer"),
|
||||
|
||||
.One => return @ptrCast(if (pointer_info.is_const) [*]const u8
|
||||
else [*]u8, pointer)[0 .. @sizeOf(Pointer)],
|
||||
|
||||
.Slice => return @ptrCast(if (pointer_info.is_const) [*]const u8 else
|
||||
[*]u8, pointer.ptr)[0 .. (@sizeOf(Pointer) * pointer.len)],
|
||||
}
|
||||
}
|
||||
|
||||
test "Bytes of types" {
|
||||
const testing = std.testing;
|
||||
|
||||
var foo: u32 = 10;
|
||||
|
||||
testing.expectEqual(bytes(&foo), 0x0a);
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns `true` if `this` is the same length and contains the same data as `that`, otherwise
|
||||
/// `false`.
|
||||
|
|
|
@ -5,7 +5,13 @@ const std = @import("std");
|
|||
///
|
||||
///
|
||||
pub const Archive = struct {
|
||||
pub fn deinit(archive: *Archive) {
|
||||
|
||||
}
|
||||
|
||||
pub fn init(file_system: *const sys.FileSystem, file_path: sys.Path) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
const core = @import("./core.zig");
|
||||
const sys = @import("./sys.zig");
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const Archive = struct {
|
||||
file_system: *const sys.FileSystem,
|
||||
archive_path: sys.Path,
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
const Header = extern struct {
|
||||
signature: [signature_magic.len]u8,
|
||||
revision: u8,
|
||||
entry_offset: u64,
|
||||
padding: [500]u8 = std.mem.zeroes([500]u8),
|
||||
|
||||
///
|
||||
/// Magic identifier used to validate [Entry] data.
|
||||
///
|
||||
const signature_magic = [3]u8{'o', 'a', 'r'};
|
||||
|
||||
comptime {
|
||||
const size = @sizeOf(@This());
|
||||
|
||||
if (size != 512)
|
||||
@compileError("Header is " ++
|
||||
std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// An entry block of an Oar archive file.
|
||||
///
|
||||
/// Typically, following the block in memory is the file data it holds the meta-information for.
|
||||
///
|
||||
const Entry = extern struct {
|
||||
signature: [signature_magic.len]u8 = signature_magic,
|
||||
revision: u8,
|
||||
path: Path,
|
||||
file_size: u64,
|
||||
absolute_offset: u64,
|
||||
padding: [232]u8 = std.mem.zeroes([232]u8),
|
||||
|
||||
comptime {
|
||||
const entry_size = @sizeOf(Entry);
|
||||
|
||||
if (entry_size != 512)
|
||||
@compileError("Entry is " ++
|
||||
std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const OpenError = error {
|
||||
FileNotFound,
|
||||
EntryNotFound,
|
||||
UnsupportedArchive,
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn open(archive: Archive, entry_path: Path) OpenError!EntryAccess {
|
||||
const file_access = try archive.file_system.open(entry_path, .readonly);
|
||||
|
||||
errdefer file_access.close();
|
||||
|
||||
var header = std.mem.zeroes(Header);
|
||||
const header_size = @sizeOf(Header);
|
||||
const io = core.io;
|
||||
|
||||
// Validate header.
|
||||
if ((try file_access.read(io.bytes(&header)) != header_size) or header
|
||||
(!core.io.equals(u8, &header.signature, &Header.signature_magic)) or
|
||||
(header.revision != revision) or
|
||||
(header.entry_offset <= header_size)) return error.UnsupportedArchive;
|
||||
|
||||
// Go to file table.
|
||||
try file_access.seek(header.entry_offset);
|
||||
|
||||
// Read file table.
|
||||
var entry_buffer = std.mem.zeroes([8]Entry);
|
||||
var bytes_read = try file_access.read(io.bytes(&entry_buffer));
|
||||
|
||||
while (@mod(bytes_read, @sizeOf(Entry)) == 0) {
|
||||
for (entry_buffer[0 .. (bytes_read / @sizeOf(Entry))]) |entry| {
|
||||
if (entry.path.equals(entry_path)) {
|
||||
file_access.seek(entry.file_offset);
|
||||
|
||||
return Entry{
|
||||
.file_access = file_access,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bytes_read = try file_access.read(io.bytes(&entry_buffer));
|
||||
}
|
||||
|
||||
var head = std.mem.zeroes(usize);
|
||||
var tail = (header.file_count - 1);
|
||||
|
||||
while (head <= tail) {
|
||||
const midpoint = (head + (tail - head) / 2);
|
||||
|
||||
const comparison = entry_path.compare(arr[m]));
|
||||
|
||||
if (comparison == 0) return midpoint;
|
||||
|
||||
if (comparison > 0) {
|
||||
// If x greater, ignore left half
|
||||
head = (midpoint + 1);
|
||||
} else {
|
||||
// If x is smaller, ignore right half
|
||||
tail = (midpoint - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return error.EntryNotFound;
|
||||
}
|
||||
};
|
||||
|
||||
pub const EntryAccess = struct {
|
||||
file_access: FileAccess,
|
||||
|
||||
pub fn close(entry: Entry) void {
|
||||
entry.file_access.close();
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
const revision = 0;
|
|
@ -100,37 +100,6 @@ pub const FileSystem = union(enum) {
|
|||
native: []const u8,
|
||||
archive: Archive,
|
||||
|
||||
///
|
||||
/// Archive file system information.
|
||||
///
|
||||
const Archive = struct {
|
||||
file_access: core.io.FileAccess,
|
||||
index_cache: IndexCache,
|
||||
entry_table: [max_open_entries]Entry = std.mem.zeroes([max_open_entries]Entry),
|
||||
|
||||
///
|
||||
/// Hard limit on the maximum number of entries open at once.
|
||||
///
|
||||
const max_open_entries = 16;
|
||||
|
||||
///
|
||||
/// Stateful extension of an [oar.Entry].
|
||||
///
|
||||
const Entry = struct {
|
||||
owner: ?*core.io.FileAccess,
|
||||
cursor: u64,
|
||||
header: oar.Entry,
|
||||
};
|
||||
|
||||
///
|
||||
/// Table cache for associating [oar.Path] values with offsets to entries in a given file.
|
||||
///
|
||||
const IndexCache = core.table.Hashed(oar.Path, u64, .{
|
||||
.equals = oar.Path.equals,
|
||||
.hash = oar.Path.hash,
|
||||
});
|
||||
};
|
||||
|
||||
///
|
||||
/// With files typically being backed by a block device, they can produce a variety of errors -
|
||||
/// from physical to virtual errors - these are all encapsulated by the API as general
|
||||
|
@ -172,7 +141,9 @@ pub const FileSystem = union(enum) {
|
|||
/// Returns a [FileAccess] reference that provides access to the file referenced by `path`or a
|
||||
/// [OpenError] if it failed.
|
||||
///
|
||||
pub fn open(file_system: *FileSystem, path: Path, mode: OpenMode) OpenError!core.io.FileAccess {
|
||||
pub fn open(file_system: *const FileSystem, path: Path,
|
||||
mode: OpenMode) OpenError!core.io.FileAccess {
|
||||
|
||||
switch (file_system.*) {
|
||||
.archive => |*archive| {
|
||||
if (mode != .readonly) return error.ModeUnsupported;
|
||||
|
|
Loading…
Reference in New Issue