DataReader!
This commit is contained in:
+9
-16
@@ -32,10 +32,7 @@ pub fn init(alloc: std.mem.Allocator, archive: Archive, entry: Directory.Entry)
|
||||
return .{
|
||||
.file = archive.file,
|
||||
.super = archive.super,
|
||||
.decomp = .{
|
||||
.alloc = alloc,
|
||||
.vtable = &.{ .stateless = archive.stateless_decomp.vtable.stateless },
|
||||
},
|
||||
.decomp = archive.stateless_decomp.statelessCopy(alloc),
|
||||
.name = new_name,
|
||||
.inode = try Utils.readInode(
|
||||
alloc,
|
||||
@@ -63,8 +60,6 @@ pub fn isDir(self: File) bool {
|
||||
}
|
||||
/// Opens a sub-file. If the given path is "" or "." (after trimming /) a copy of the File is returned.
|
||||
pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File {
|
||||
switch (self.inode.hdr.inode_type) {
|
||||
.dir, .ext_dir => {
|
||||
var res = try self.inode.findInode(
|
||||
alloc,
|
||||
&self.decomp,
|
||||
@@ -81,16 +76,10 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File {
|
||||
return .{
|
||||
.file = self.file,
|
||||
.super = self.super,
|
||||
.decomp = .{
|
||||
.alloc = alloc,
|
||||
.vtable = &.{ .stateless = self.decomp.vtable.stateless },
|
||||
},
|
||||
.decomp = self.decomp.statelessCopy(alloc),
|
||||
.name = res.name,
|
||||
.inode = res.inode,
|
||||
};
|
||||
},
|
||||
else => Error.NotDirectory,
|
||||
}
|
||||
}
|
||||
pub fn iter(self: File, alloc: std.mem.Allocator) !FileIter {
|
||||
return .{
|
||||
@@ -107,10 +96,14 @@ pub fn isRegularFile(self: File) bool {
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
// a std.Io.Reader compatible reader that reads a regular file's data.
|
||||
pub fn dataReader(self: File, alloc: std.mem.Allocator) !DataReader {
|
||||
if (!self.isRegularFile()) return Error.NotRegularFile;
|
||||
_ = alloc;
|
||||
return error.TODO;
|
||||
return self.inode.dataReader(
|
||||
&self.decomp.statelessCopy(alloc),
|
||||
self.file,
|
||||
self.super.frag_start,
|
||||
self.super.block_size,
|
||||
);
|
||||
}
|
||||
|
||||
// Universal functions
|
||||
|
||||
+43
-1
@@ -7,11 +7,14 @@ const Reader = std.Io.Reader;
|
||||
|
||||
const Decompressor = @import("decomp.zig");
|
||||
const Directory = @import("directory.zig");
|
||||
const FragEntry = @import("archive.zig").FragEntry;
|
||||
const Dir = @import("inode/dir.zig");
|
||||
const File = @import("inode/file.zig");
|
||||
const Misc = @import("inode/misc.zig");
|
||||
const Sym = @import("inode/sym.zig");
|
||||
const LookupTable = @import("lookup_table.zig");
|
||||
const MinimalSuperblock = @import("archive.zig").MinimalSuperblock;
|
||||
const DataReader = @import("util/data_reader.zig");
|
||||
const MetadataReader = @import("util/metadata.zig");
|
||||
const OffsetFile = @import("util/offset_file.zig");
|
||||
|
||||
@@ -183,7 +186,19 @@ pub const Error = error{
|
||||
|
||||
// Utils functions
|
||||
|
||||
/// For directory inodes, tries to find the inode at the given path. Returns both the inode, and it's file name. If the path is empty or "." then a copy of this inode is returned with no name ("").
|
||||
// Universal
|
||||
|
||||
pub fn uid(self: Inode, decomp: *const Decompressor, fil: OffsetFile, id_start: u64) !u16 {
|
||||
return LookupTable.stateless(u16, fil, decomp, id_start, self.hdr.uid_idx);
|
||||
}
|
||||
pub fn gid(self: Inode, decomp: *const Decompressor, fil: OffsetFile, id_start: u64) !u16 {
|
||||
return LookupTable.stateless(u16, fil, decomp, id_start, self.hdr.gid_idx);
|
||||
}
|
||||
|
||||
// Dir inodes
|
||||
|
||||
/// For directory inodes, tries to find the inode at the given path. Returns both the inode, and it's file name.
|
||||
/// If the path is empty or "." then a copy of this inode is returned with no name ("").
|
||||
pub fn findInode(
|
||||
inode: Inode,
|
||||
alloc: std.mem.Allocator,
|
||||
@@ -279,6 +294,7 @@ inline fn findInodeRaw(
|
||||
return inode.findInode(alloc, decomp, fil, dir_start, inode_start, block_size, path[first_element.len..]);
|
||||
}
|
||||
|
||||
/// Get the directory entries for a directory inode.
|
||||
pub fn readDirectory(inode: Inode, alloc: std.mem.Allocator, decomp: *const Decompressor, fil: OffsetFile, dir_start: u64) ![]Directory.Entry {
|
||||
return switch (inode.data) {
|
||||
.dir => |d| readDirRaw(alloc, decomp, fil, dir_start, d),
|
||||
@@ -292,3 +308,29 @@ inline fn readDirRaw(alloc: std.mem.Allocator, decomp: *const Decompressor, fil:
|
||||
try meta_rdr.interface.discardAll(dat.block_offset);
|
||||
return Directory.readDirectory(alloc, meta_rdr, dat.size);
|
||||
}
|
||||
|
||||
// file inodes
|
||||
|
||||
/// Gets the data reader for a file inode.
|
||||
pub fn dataReader(inode: Inode, decomp: *const Decompressor, fil: OffsetFile, frag_start: u64, block_size: u32) !DataReader {
|
||||
return switch (inode.data) {
|
||||
.file => |f| dataReaderRaw(decomp, fil, frag_start, block_size, f),
|
||||
.ext_file => |f| dataReaderRaw(decomp, fil, frag_start, block_size, f),
|
||||
else => Error.NotRegularFile,
|
||||
};
|
||||
}
|
||||
inline fn dataReaderRaw(decomp: *const Decompressor, fil: OffsetFile, frag_start: u64, block_size: u32, dat: anytype) !DataReader {
|
||||
return .init(
|
||||
decomp,
|
||||
fil,
|
||||
block_size,
|
||||
dat.block_sizes,
|
||||
dat.size,
|
||||
dat.block_start,
|
||||
if (dat.frag_idx != 0xFFFFFFFF)
|
||||
try LookupTable.stateless(FragEntry, fil, decomp, frag_start, dat.frag_idx)
|
||||
else
|
||||
null,
|
||||
dat.frag_offset,
|
||||
);
|
||||
}
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@ pub const BlockSize = packed struct {
|
||||
pub const File = struct {
|
||||
block_start: u32,
|
||||
frag_idx: u32,
|
||||
block_offset: u32,
|
||||
frag_offset: u32,
|
||||
size: u32,
|
||||
block_sizes: []BlockSize,
|
||||
|
||||
@@ -27,7 +27,7 @@ pub const File = struct {
|
||||
return .{
|
||||
.block_start = std.mem.readVarInt(u32, buf[0..4], .little),
|
||||
.frag_idx = frag_idx,
|
||||
.block_offset = std.mem.readVarInt(u32, buf[8..12], .little),
|
||||
.frag_offset = std.mem.readVarInt(u32, buf[8..12], .little),
|
||||
.size = size,
|
||||
.block_sizes = sizes,
|
||||
};
|
||||
@@ -40,7 +40,7 @@ pub const ExtFile = struct {
|
||||
sparse: u64,
|
||||
hard_links: u32,
|
||||
frag_idx: u32,
|
||||
block_offset: u32,
|
||||
frag_offset: u32,
|
||||
xattr_idx: u32,
|
||||
block_sizes: []BlockSize,
|
||||
|
||||
@@ -61,7 +61,7 @@ pub const ExtFile = struct {
|
||||
.sparse = std.mem.readVarInt(u64, buf[16..24], .little),
|
||||
.hard_links = std.mem.readVarInt(u32, buf[24..28], .little),
|
||||
.frag_idx = frag_idx,
|
||||
.block_offset = std.mem.readVarInt(u32, buf[32..36], .little),
|
||||
.frag_offset = std.mem.readVarInt(u32, buf[32..36], .little),
|
||||
.xattr_idx = std.mem.readVarInt(u32, buf[36..40], .little),
|
||||
.block_sizes = sizes,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
const std = @import("std");
|
||||
const Reader = std.Io.Reader;
|
||||
const Writer = std.Io.Writer;
|
||||
const Limit = std.Io.Limit;
|
||||
|
||||
const FragEntry = @import("../archive.zig").FragEntry;
|
||||
const Decompressor = @import("../decomp.zig");
|
||||
const BlockSize = @import("../inode/file.zig").BlockSize;
|
||||
const OffsetFile = @import("offset_file.zig");
|
||||
|
||||
const DataReader = @This();
|
||||
|
||||
decomp: *const Decompressor,
|
||||
file: OffsetFile,
|
||||
block_size: u32,
|
||||
blocks: []BlockSize,
|
||||
size: u64,
|
||||
frag: ?FragEntry,
|
||||
frag_offset: u32,
|
||||
|
||||
offset: u64,
|
||||
idx: usize = 0,
|
||||
sparse: bool = false,
|
||||
|
||||
interface: Reader,
|
||||
|
||||
pub fn init(decomp: *const Decompressor, file: OffsetFile, block_size: u32, blocks: []BlockSize, size: u64, init_offset: u64, frag: ?FragEntry, frag_offset: u32) DataReader {
|
||||
return .{
|
||||
.decomp = decomp,
|
||||
.file = file,
|
||||
.block_size = block_size,
|
||||
.blocks = blocks,
|
||||
.size = size,
|
||||
.frag = frag,
|
||||
.frag_offset = frag_offset,
|
||||
|
||||
.offset = init_offset,
|
||||
|
||||
.interface = .{
|
||||
.buffer = &[1]u8{undefined} ** (1024 * 1024),
|
||||
.end = 0,
|
||||
.seek = 0,
|
||||
.vtable = &.{ .stream = stream, .discard = discard, .readVec = readVec },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn numBlocks(self: *DataReader) usize {
|
||||
return if (self.frag == null)
|
||||
self.blocks.len
|
||||
else
|
||||
self.blocks.len + 1;
|
||||
}
|
||||
fn advanceBuffer(self: *DataReader) Reader.Error!void {
|
||||
if (self.idx >= self.numBlocks()) return Reader.Error.EndOfStream;
|
||||
defer self.idx += 1;
|
||||
self.sparse = false;
|
||||
self.interface.end = 0; // If we error out and the error is ignored, we'll stil end up back here to error again.
|
||||
self.interface.seek = 0;
|
||||
if (self.idx == self.blocks.len) { // Fragment
|
||||
var rdr = self.file.readerAt(self.frag.?.block_start, &[0]u8{}) catch return Reader.Error.ReadFailed;
|
||||
const size = self.size % self.block_size;
|
||||
if (self.frag.?.size.uncompressed) {
|
||||
try rdr.interface.discardAll(self.frag_offset);
|
||||
try rdr.interface.readSliceAll(self.interface.buffer[0..size]);
|
||||
self.interface.end = size;
|
||||
return;
|
||||
}
|
||||
const raw_loc = self.interface.buffer.len - self.frag.?.size.size;
|
||||
try rdr.interface.readSliceAll(self.interface.buffer[raw_loc..]);
|
||||
_ = self.decomp.decompress(self.interface.buffer[raw_loc..], self.interface.buffer) catch
|
||||
return Reader.Error.ReadFailed;
|
||||
@memmove(self.interface.buffer[0..size], self.interface.buffer[self.frag_offset .. self.frag_offset + size]);
|
||||
self.interface.end = size;
|
||||
return;
|
||||
}
|
||||
const block = self.blocks[self.idx];
|
||||
if (block.size == 0) {
|
||||
self.interface.end = if (self.idx == self.numBlocks() - 1)
|
||||
self.size % self.block_size
|
||||
else
|
||||
self.block_size;
|
||||
self.sparse = true;
|
||||
return;
|
||||
}
|
||||
defer self.offset += block.size;
|
||||
var rdr = try self.file.readerAt(self.offset, &[0]u8{});
|
||||
if (block.uncompressed) {
|
||||
try rdr.interface.readSliceAll(self.interface.buffer[0..block.size]);
|
||||
self.interface.end = block.size;
|
||||
return;
|
||||
}
|
||||
const raw_loc = self.interface.buffer.len - block.size;
|
||||
try rdr.interface.readSliceAll(self.interface.buffer[raw_loc..]);
|
||||
self.interface.end = self.decomp.decompress(self.interface.buffer[raw_loc..], self.interface.buffer) catch
|
||||
return Reader.Error.ReadFailed;
|
||||
}
|
||||
|
||||
fn stream(r: *Reader, wrt: *Writer, limit: Limit) Reader.StreamError!usize {
|
||||
var self: *DataReader = @fieldParentPtr("interface", r);
|
||||
if (r.seek == r.end) try self.advanceBuffer();
|
||||
if (limit == .nothing) return 0;
|
||||
|
||||
const to_write = @min(r.end - r.seek, @intFromEnum(limit));
|
||||
const wrote = if (self.sparse)
|
||||
try wrt.splatByte(0, to_write)
|
||||
else
|
||||
try wrt.write(r.buffer[r.seek .. r.seek + to_write]);
|
||||
r.seek += wrote;
|
||||
return wrote;
|
||||
}
|
||||
fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
|
||||
var self: *DataReader = @fieldParentPtr("interface", r);
|
||||
if (r.seek == r.end) try self.advanceBuffer();
|
||||
if (limit == .nothing) return 0;
|
||||
|
||||
const adv = @min(r.end - r.seek, @intFromEnum(limit));
|
||||
r.seek += adv;
|
||||
return adv;
|
||||
}
|
||||
fn readVec(r: *Reader, vec: [][]u8) Reader.Error!usize {
|
||||
var self: *DataReader = @fieldParentPtr("interface", r);
|
||||
if (r.seek == r.end) try self.advanceBuffer();
|
||||
|
||||
var wrote: usize = 0;
|
||||
for (vec) |slice| {
|
||||
if (r.seek == r.end) break;
|
||||
const to_copy = @min(r.end - r.seek, slice.len);
|
||||
if (self.sparse) {
|
||||
@memset(slice[0..to_copy], 0);
|
||||
} else {
|
||||
@memcpy(slice[0..to_copy], r.buffer[r.seek .. r.seek + to_copy]);
|
||||
}
|
||||
r.seek += to_copy;
|
||||
wrote += to_copy;
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user