Application Context Implementation #4
|
@ -204,6 +204,38 @@ test "Check memory begins with" {
|
||||||
try testing.expect(!begins(u8, &.{69, 89, 42, 0}, bytes_sequence));
|
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
|
/// Returns `true` if `this` is the same length and contains the same data as `that`, otherwise
|
||||||
/// `false`.
|
/// `false`.
|
||||||
|
|
|
@ -5,7 +5,13 @@ const std = @import("std");
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub const Archive = struct {
|
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 {
|
||||||
kayomn marked this conversation as resolved
Outdated
|
|||||||
|
const size = @sizeOf(@This());
|
||||||
|
|
||||||
|
if (size != 512)
|
||||||
|
@compileError("Header is " ++
|
||||||
|
std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
|
||||||
|
}
|
||||||
|
};
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
|
||||||
|
///
|
||||||
|
/// 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,
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
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.
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
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);
|
||||||
|
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
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]));
|
||||||
|
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
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,
|
native: []const u8,
|
||||||
archive: Archive,
|
archive: Archive,
|
||||||
|
|
||||||
kayomn marked this conversation as resolved
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
///
|
|
||||||
/// 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 -
|
/// 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
|
/// 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
|
/// Returns a [FileAccess] reference that provides access to the file referenced by `path`or a
|
||||||
/// [OpenError] if it failed.
|
/// [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.*) {
|
switch (file_system.*) {
|
||||||
.archive => |*archive| {
|
.archive => |*archive| {
|
||||||
if (mode != .readonly) return error.ModeUnsupported;
|
if (mode != .readonly) return error.ModeUnsupported;
|
||||||
|
|
Loading…
Reference in New Issue
Missing documentation comment.