Initial work merging Oar into Ona itself
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details

This commit is contained in:
kayomn 2022-10-26 18:09:59 +01:00
parent 32b18e4ebf
commit 7599ce61f2
4 changed files with 179 additions and 32 deletions

View File

@ -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`.

View File

@ -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) {
}
};
///

138
src/ona/oar.zig Normal file
View File

@ -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;

View File

@ -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;