Some work for threaded extraction

This commit is contained in:
Caleb J. Gardner
2026-02-08 10:27:35 -06:00
parent 2760ad6ccb
commit b892adacd7
2 changed files with 42 additions and 19 deletions
+1 -2
View File
@@ -62,8 +62,7 @@ pub fn init(alloc: std.mem.Allocator, fil: File) !Archive {
/// Create the Archive dictating the amount of threads & memory used.
/// If trying to extract a full archive, a large memory size & thread count could help.
/// If you're planning on only interacting with a small number of files, it should be fine to use few threads and a small memory size.
pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: usize, mem: usize) !Archive {
_ = mem;
pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: usize) !Archive {
var super: Superblock = undefined;
const red = try fil.pread(@ptrCast(&super), offset);
std.debug.assert(red == @sizeOf(Superblock));
+41 -17
View File
@@ -91,6 +91,12 @@ pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !Inode {
},
};
}
pub fn readFromEntry(alloc: std.mem.Allocator, archive: *Archive, entry: DirEntry) !Inode {
var rdr = try archive.fil.readerAt(archive.super.inode_start + entry.block_start, &[0]u8{});
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp);
try meta.interface.discardAll(entry.block_offset);
return read(alloc, &meta.interface, archive.super.block_size);
}
pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
switch (self.data) {
@@ -118,16 +124,15 @@ fn readerFromData(archive: *Archive, data: anytype) !DataReader {
}
/// Get the directory entries for a directory inode.
pub fn dirEntries(self: Inode, archive: *Archive) ![]DirEntry {
pub fn dirEntries(self: Inode, alloc: std.mem.Allocator, archive: *Archive) ![]DirEntry {
return switch (self.hdr.inode_type) {
.dir => entriesFromData(archive, self.data.dir),
.ext_dir => entriesFromData(archive, self.data.ext_dir),
.dir => entriesFromData(alloc, archive, self.data.dir),
.ext_dir => entriesFromData(alloc, archive, self.data.ext_dir),
else => error.NotDirectory,
};
}
fn entriesFromData(archive: *Archive, data: anytype) ![]DirEntry {
fn entriesFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) ![]DirEntry {
var rdr = try archive.fil.readerAt(archive.super.dir_start + data.block_start, &[0]u8{});
const alloc = archive.allocator();
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp);
try meta.interface.discardAll(data.block_offset);
return DirEntry.readDir(alloc, &meta.interface, data.size);
@@ -155,10 +160,7 @@ pub fn extractTo(self: Inode, archive: *Archive, path: []const u8, options: Extr
new_path[path.len] = '/';
defer alloc.free(new_path);
var rdr = try archive.fil.readerAt(archive.super.inode_start + entry.block_start, &[0]u8{});
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp);
try meta.interface.discardAll(entry.block_offset);
var inode: Inode = try read(alloc, &meta.interface, archive.super.block_size);
var inode: Inode = try readFromEntry(archive, entry);
defer inode.deinit(alloc);
try inode.extractTo(archive, new_path, options);
}
@@ -171,9 +173,9 @@ pub fn extractTo(self: Inode, archive: *Archive, path: []const u8, options: Extr
const Perms = struct {
path: []const u8,
owner: u16,
uid: u16,
gid: u16,
perm: u16,
mod_time: u32,
};
/// Extract the inode to the given path. Multi-threaded.
@@ -182,12 +184,34 @@ const Perms = struct {
/// If threads <= 1, then this just calls extractTo.
pub fn extractToThreaded(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions, threads: usize) !void {
if (threads <= 1) return self.extractTo(archive, path, options);
std.debug.print("{}\n", .{threads});
@constCast(&threads).* = try std.Thread.getCpuCount();
std.debug.print("{}\n", .{threads});
switch (self.hdr.inode_type) {
.dir, .ext_dir => {},
.file, .ext_file => {},
.dir, .ext_dir => {
var arena_alloc: std.heap.ArenaAllocator = .init(archive.allocator());
defer arena_alloc.deinit();
var alloc = arena_alloc.allocator();
var wg: WaitGroup = .{};
var perms: ?std.ArrayList(Perms) = if (options.ignore_permissions) null else try .initCapacity(alloc, 100);
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
var pool: Pool = undefined;
try pool.init(.{ .n_jobs = threads });
const entries = try self.dirEntries(archive);
var files: std.ArrayList(*DirEntry) = try .initCapacity(alloc, 100);
// defer files.deinit(alloc); We don't need to do this due to ArenaAllocator
try self.extractThread(alloc, archive, path, options, &wg, &pool, if (perms == null) null else &perms);
wg.wait();
if (perms != null) {
for (perms.items) |p| {
var fil = try std.fs.cwd().openFile(p.path, .{});
try fil.chmod(p.perm);
try fil.chown(p.uid, p.gid);
}
}
},
.file, .ext_file => {
return error.TODO;
},
.symlink, .ext_symlink => try self.extractSymlink(path),
else => try self.extractDevice(archive, path, options),
}
@@ -195,7 +219,7 @@ pub fn extractToThreaded(self: Inode, archive: *Archive, path: []const u8, optio
}
/// Extract threadedly the inode to the path.
fn extractThread(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions, wg: *WaitGroup, pool: *Pool, perms: ?*std.ArrayList(Perms)) !void {
fn extractThread(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions, wg: *WaitGroup, pool: *Pool, perms: ?*std.ArrayList(Perms)) !void {
_ = pool;
_ = perms;
_ = archive;