From 173e76469fa579144251899efbf312e704dec79b Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Sun, 30 Aug 2026 02:37:37 -0500 Subject: [PATCH] Created DataReader (untested) Other minor fixes & tweaks --- src/archive.zig | 7 +- src/file.zig | 93 ++++++++++++++++++++++++- src/inode.zig | 2 +- src/tests.zig | 6 +- src/utils/data_reader.zig | 141 ++++++++++++++++++++++++++++++++++++++ src/utils/meta.zig | 2 +- 6 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 src/utils/data_reader.zig diff --git a/src/archive.zig b/src/archive.zig index 6d74be1..002313d 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -6,7 +6,6 @@ const util = @import("utils/util.zig"); const File = @import("file.zig"); const Inode = @import("inode.zig"); const Decomp = @import("decomp.zig"); - const Options = @import("options.zig"); const Archive = @This(); @@ -16,7 +15,7 @@ map: Io.File.MemoryMap, super: Super, root_inode_ref: Inode.Reference, -pub fn open(io: Io, file: Io.File, offset: u64) !Archive { +pub fn init(io: Io, file: Io.File, offset: u64) !Archive { var map = try file.createMemoryMap(io, .{ .len = try file.length(io) - offset, .offset = offset, @@ -33,7 +32,7 @@ pub fn open(io: Io, file: Io.File, offset: u64) !Archive { .root_inode_ref = superblock.root_inode_ref, }; } -pub fn close(self: *Archive, io: Io) void { +pub fn deinit(self: *Archive, io: Io) void { self.map.destroy(io); } @@ -53,7 +52,7 @@ pub fn root(self: *Archive, alloc: std.mem.Allocator) !File { .name = "", }; } -pub fn openFile(self: *Archive, alloc: std.mem.Allocator, filepath: []const u8) !File { +pub fn open(self: *Archive, alloc: std.mem.Allocator, filepath: []const u8) !File { var root_file = try self.root(alloc); defer root_file.deinit(); diff --git a/src/file.zig b/src/file.zig index f8cbead..03d24a7 100644 --- a/src/file.zig +++ b/src/file.zig @@ -4,6 +4,8 @@ const Io = std.Io; const Inode = @import("inode.zig"); const Directory = @import("dir.zig"); const MetadataReader = @import("utils/meta.zig"); +const Options = @import("options.zig"); +const DataReader = @import("utils/data_reader.zig"); const Super = @import("archive.zig").Super; const File = @This(); @@ -35,6 +37,31 @@ pub fn deinit(self: File) void { self.alloc.free(self.name); } +/// Reader interface to read a regular file's contents. +pub fn reader(self: File, alloc: std.mem.Allocator) !DataReader { + return switch (self.inode.header.type) { + .file => |f| .init( + alloc, + self.data, + self.super.decomp_fn, + self.super.block_size, + f.start, + f.size, + f.blocks, + ), + .ext_file => |f| .init( + alloc, + self.data, + self.super.decomp_fn, + self.super.block_size, + f.start, + f.size, + f.blocks, + ), + else => error.NotRegularFile, + }; +} + pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File { switch (self.inode.header.type) { .dir, .ext_dir => {}, @@ -47,6 +74,7 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File { return self.copy(alloc); const first_element: []u8 = std.mem.sliceTo(path, '/'); + const final = first_element.len == path.len; var dir_size: u32 = undefined; var dir_rdr: MetadataReader = undefined; @@ -67,10 +95,73 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File { else => unreachable, } - const inode: Inode = { + const entry: Directory.Entry = blk: { var dir: Directory = try .read(alloc, &dir_rdr.interface, dir_size); defer dir.deinit(alloc); + + var entries = dir.entries; + + while (entries.len > 0) { + const idx = entries.len / 2; + const val = entries[idx]; + + switch (std.mem.order(u8, first_element, val.name)) { + .eq => { + if (final) { + const inode: Inode = try .fromRef( + alloc, + self.data, + .{ .start = val.start, .offset = val.offset }, + self.super, + ); + errdefer inode.deinit(alloc); + + return .{ + .alloc = alloc, + + .data = self.data, + .super = self.super, + + .inode = inode, + .name = try alloc.dupe(u8, val.name), + }; + } + break :blk val; + }, + .lt => entries = entries[0..idx], + .gt => entries = entries[idx..], + } + } + return error.NotFound; }; + if (entry.type != .dir) + return error.NotFound; + + const transient_file: File = .{ + .alloc = alloc, + + .data = self.data, + .super = self.super, + + .inode = try .fromRef( + alloc, + self.data, + .{ .start = entry.start, .offset = entry.offset }, + self.super, + ), + .name = "", + }; + + return transient_file.open(alloc, filepath[first_element.len..]); +} + +/// Extract the given File to the location. +pub fn extract(self: File, alloc: std.mem.Allocator, io: Io, ext_loc: []const u8, options: Options) !void { + _ = self; + _ = alloc; + _ = io; + _ = ext_loc; + _ = options; return error.TODO; } diff --git a/src/inode.zig b/src/inode.zig index e6a4b85..cdf9fe3 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -246,7 +246,7 @@ pub const ExtDev = extern struct { xattr_idx: u32, pub fn read(rdr: *Io.Reader) !ExtDev { - return util.readValue(ExtDev, rdr); + return util.readValueRdr(ExtDev, rdr); } }; diff --git a/src/tests.zig b/src/tests.zig index 229d2cc..a4b765e 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -12,11 +12,11 @@ test "Open" { var archive_file = try Io.Dir.cwd().openFile(io, TEST_ARCHIVE, .{}); defer archive_file.close(io); - var arc: Archive = try .open(io, archive_file, 0); - defer arc.close(io); + var arc: Archive = try .init(io, archive_file, 0); + defer arc.deinit(io); std.debug.print("{}\n", .{arc.super}); var root = try arc.root(alloc); - defer root.close(); + defer root.deinit(); } diff --git a/src/utils/data_reader.zig b/src/utils/data_reader.zig new file mode 100644 index 0000000..25a5581 --- /dev/null +++ b/src/utils/data_reader.zig @@ -0,0 +1,141 @@ +const std = @import("std"); +const Io = std.Io; + +const Decomp = @import("../decomp.zig"); +const BlockSize = @import("../inode.zig").BlockSize; + +const DataReader = @This(); + +alloc: std.mem.Allocator, + +data: []u8, +decomp: Decomp.Fn, +block_size: u32, + +size: u64, +blocks: []BlockSize, + +frag: ?[]u8 = null, + +idx: u32 = 0, +buf: []u8, + +interface: Io.Reader = .{ + .buffer = &[0]u8{}, + .seek = 0, + .end = 0, + .vtable = &.{ + .stream = stream, + .discard = discard, + .readVec = readVec, + }, +}, + +pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, block_size: u32, start: u64, size: u64, blocks: []BlockSize) DataReader { + return .{ + .alloc = alloc, + + .data = data[start..], + .decomp = decomp, + .block_size = block_size, + + .size = size, + .blocks = blocks, + .buf = if (blocks.len > 0) try alloc.alloc(u8, 1024 * 1024) else &[0]u8{}, + }; +} +pub fn deinit(self: DataReader) void { + self.alloc.free(self.buf); +} + +pub fn addFrag(self: *DataReader, frag: []u8) void { + self.frag = frag; +} + +fn advance(self: *DataReader) Io.Reader.Error!void { + if (self.idx > self.blocks.len) return error.EndOfStream; + defer self.idx += 1; + + self.interface.seek = 0; + errdefer self.interface.end = 0; + + if (self.idx == self.blocks.len) { + if (self.frag == null) return error.EndOfStream; + + self.interface.buffer = self.frag.?; + self.interface.end = self.frag.?.len; + return; + } + + self.interface.end = if (self.frag == null and self.idx == self.blocks.len - 1) + self.size % self.block_size + else + self.block_size; + + const block = self.blocks[self.idx]; + + if (block.size == 0) { + @memset(self.buf[0..self.interface.end], 0); + self.interface.buffer = self.buf; + return; + } + + if (block.uncompressed) { + self.interface.buffer = self.data; + self.data = self.data[self.interface.end..]; + return; + } + + _ = self.decomp(self.alloc, self.data[0..block.size], self.buf[0..self.interface.end]) catch return error.ReadFailed; + self.data = self.data[block.size..]; + + self.interface.buffer = self.buf; + return; +} + +fn stream(rdr: *Io.Reader, wrt: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { + if (rdr.seek >= rdr.end) { + var meta: *DataReader = @fieldParentPtr("interface", rdr); + try meta.advance(); + } + if (limit == .nothing) return 0; + + const size = @min(@intFromEnum(limit), rdr.end - rdr.seek); + + const wrote = try wrt.write(rdr.buffer[rdr.seek..][0..size]); + rdr.seek += wrote; + + return wrote; +} +fn discard(rdr: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { + if (rdr.seek >= rdr.end) { + var meta: *DataReader = @fieldParentPtr("interface", rdr); + try meta.advance(); + } + if (limit == .nothing) return 0; + + const size = @min(@intFromEnum(limit), rdr.end - rdr.seek); + rdr.seek += size; + + return size; +} +fn readVec(rdr: *Io.Reader, vec: [][]u8) Io.Reader.Error!usize { + if (rdr.seek >= rdr.end) { + var meta: *DataReader = @fieldParentPtr("interface", rdr); + try meta.advance(); + } + + var wrote: usize = 0; + for (vec) |s| { + const size = @min(rdr.end - rdr.seek, s.len); + + @memcpy(s[0..size], rdr.buffer[rdr.seek..][0..size]); + + wrote += size; + rdr.seek += size; + + if (rdr.seek >= rdr.end) break; + } + + return wrote; +} diff --git a/src/utils/meta.zig b/src/utils/meta.zig index b7c1d65..6a79555 100644 --- a/src/utils/meta.zig +++ b/src/utils/meta.zig @@ -15,7 +15,7 @@ decomp: Decomp.Fn, decomp_block: [8192]u8 = undefined, interface: Io.Reader = .{ - .buffer = &[0]u8, + .buffer = &[0]u8{}, .end = 0, .seek = 0, .vtable = &.{