const std = @import("std"); const sys = @import("./sys.zig"); const table = @import("./table.zig"); /// /// /// pub const Archive = struct { file_access: sys.FileAccess, index_cache: IndexCache, /// /// [OpenError.EntryNotFound] happens when an entry could not be found. /// pub const FindError = sys.FileAccess.Error || error { EntryNotFound, }; /// /// /// pub const InitError = std.mem.Allocator.Error; /// /// /// const IndexCache = table.Hashed([]const u8, Entry.Header, table.string_context); /// /// Finds an entry matching `entry_path` in `archive`. /// /// The found [Entry] value is returned or a [FindError] if it failed to be found. /// pub fn find(archive: *Archive, entry_path: []const u8) FindError!Entry { return Entry{ .header = find_header: { if (archive.index_cache.lookup(entry_path)) |entry_header| break: find_header entry_header.*; // Start from beginning of archive. try archive.file_access.seek(0); var entry_header = Entry.Header{ .revision = 0, .file_size = 0, .file_offset = 0 }; const read_buffer = std.mem.asBytes(&entry_header); // Read first entry. while ((try archive.file_access.read(read_buffer)) == @sizeOf(Entry.Header)) { if (std.mem.eql(u8, entry_path, entry_header. name_buffer[0 .. entry_header.name_length])) { // If caching fails... oh well... archive.index_cache.insert(entry_path, entry_header) catch {}; break: find_header entry_header; } // Move over file data following the entry. var to_skip = entry_header.file_size; while (to_skip != 0) { const skipped = std.math.min(to_skip, std.math.maxInt(i64)); try archive.file_access.skip(@intCast(i64, skipped)); to_skip -= skipped; } } return error.EntryNotFound; }, .owner = &archive.file_access, .cursor = 0, }; } /// /// /// pub fn init(allocator: std.mem.Allocator, archive_file_access: sys.FileAccess) InitError!Archive { return Archive{ .index_cache = try IndexCache.init(allocator), .file_access = archive_file_access, }; } }; /// /// Handles the state of an opened archive entry. /// pub const Entry = struct { owner: ?*sys.FileAccess, cursor: u64, header: Header, /// /// An entry block of an Oar archive file. /// /// Typically, following the block in memory is the file data it holds the meta-information for. /// pub const Header = extern struct { signature: [signature_magic.len]u8 = signature_magic, revision: u8, name_buffer: [255]u8 = std.mem.zeroes([255]u8), name_length: u8 = 0, file_size: u64, file_offset: u64, padding: [232]u8 = std.mem.zeroes([232]u8), comptime { const entry_size = @sizeOf(Header); if (entry_size != 512) @compileError("Entry is " ++ std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes"); } /// /// Magic identifier used to validate [Entry] data. /// pub const signature_magic = [3]u8{'o', 'a', 'r'}; }; };