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 .{
|
return .{
|
||||||
.file = archive.file,
|
.file = archive.file,
|
||||||
.super = archive.super,
|
.super = archive.super,
|
||||||
.decomp = .{
|
.decomp = archive.stateless_decomp.statelessCopy(alloc),
|
||||||
.alloc = alloc,
|
|
||||||
.vtable = &.{ .stateless = archive.stateless_decomp.vtable.stateless },
|
|
||||||
},
|
|
||||||
.name = new_name,
|
.name = new_name,
|
||||||
.inode = try Utils.readInode(
|
.inode = try Utils.readInode(
|
||||||
alloc,
|
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.
|
/// 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 {
|
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(
|
var res = try self.inode.findInode(
|
||||||
alloc,
|
alloc,
|
||||||
&self.decomp,
|
&self.decomp,
|
||||||
@@ -81,16 +76,10 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File {
|
|||||||
return .{
|
return .{
|
||||||
.file = self.file,
|
.file = self.file,
|
||||||
.super = self.super,
|
.super = self.super,
|
||||||
.decomp = .{
|
.decomp = self.decomp.statelessCopy(alloc),
|
||||||
.alloc = alloc,
|
|
||||||
.vtable = &.{ .stateless = self.decomp.vtable.stateless },
|
|
||||||
},
|
|
||||||
.name = res.name,
|
.name = res.name,
|
||||||
.inode = res.inode,
|
.inode = res.inode,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
else => Error.NotDirectory,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn iter(self: File, alloc: std.mem.Allocator) !FileIter {
|
pub fn iter(self: File, alloc: std.mem.Allocator) !FileIter {
|
||||||
return .{
|
return .{
|
||||||
@@ -107,10 +96,14 @@ pub fn isRegularFile(self: File) bool {
|
|||||||
else => false,
|
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 {
|
pub fn dataReader(self: File, alloc: std.mem.Allocator) !DataReader {
|
||||||
if (!self.isRegularFile()) return Error.NotRegularFile;
|
return self.inode.dataReader(
|
||||||
_ = alloc;
|
&self.decomp.statelessCopy(alloc),
|
||||||
return error.TODO;
|
self.file,
|
||||||
|
self.super.frag_start,
|
||||||
|
self.super.block_size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Universal functions
|
// Universal functions
|
||||||
|
|||||||
+43
-1
@@ -7,11 +7,14 @@ const Reader = std.Io.Reader;
|
|||||||
|
|
||||||
const Decompressor = @import("decomp.zig");
|
const Decompressor = @import("decomp.zig");
|
||||||
const Directory = @import("directory.zig");
|
const Directory = @import("directory.zig");
|
||||||
|
const FragEntry = @import("archive.zig").FragEntry;
|
||||||
const Dir = @import("inode/dir.zig");
|
const Dir = @import("inode/dir.zig");
|
||||||
const File = @import("inode/file.zig");
|
const File = @import("inode/file.zig");
|
||||||
const Misc = @import("inode/misc.zig");
|
const Misc = @import("inode/misc.zig");
|
||||||
const Sym = @import("inode/sym.zig");
|
const Sym = @import("inode/sym.zig");
|
||||||
|
const LookupTable = @import("lookup_table.zig");
|
||||||
const MinimalSuperblock = @import("archive.zig").MinimalSuperblock;
|
const MinimalSuperblock = @import("archive.zig").MinimalSuperblock;
|
||||||
|
const DataReader = @import("util/data_reader.zig");
|
||||||
const MetadataReader = @import("util/metadata.zig");
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
const OffsetFile = @import("util/offset_file.zig");
|
const OffsetFile = @import("util/offset_file.zig");
|
||||||
|
|
||||||
@@ -183,7 +186,19 @@ pub const Error = error{
|
|||||||
|
|
||||||
// Utils functions
|
// 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(
|
pub fn findInode(
|
||||||
inode: Inode,
|
inode: Inode,
|
||||||
alloc: std.mem.Allocator,
|
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..]);
|
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 {
|
pub fn readDirectory(inode: Inode, alloc: std.mem.Allocator, decomp: *const Decompressor, fil: OffsetFile, dir_start: u64) ![]Directory.Entry {
|
||||||
return switch (inode.data) {
|
return switch (inode.data) {
|
||||||
.dir => |d| readDirRaw(alloc, decomp, fil, dir_start, d),
|
.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);
|
try meta_rdr.interface.discardAll(dat.block_offset);
|
||||||
return Directory.readDirectory(alloc, meta_rdr, dat.size);
|
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 {
|
pub const File = struct {
|
||||||
block_start: u32,
|
block_start: u32,
|
||||||
frag_idx: u32,
|
frag_idx: u32,
|
||||||
block_offset: u32,
|
frag_offset: u32,
|
||||||
size: u32,
|
size: u32,
|
||||||
block_sizes: []BlockSize,
|
block_sizes: []BlockSize,
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ pub const File = struct {
|
|||||||
return .{
|
return .{
|
||||||
.block_start = std.mem.readVarInt(u32, buf[0..4], .little),
|
.block_start = std.mem.readVarInt(u32, buf[0..4], .little),
|
||||||
.frag_idx = frag_idx,
|
.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,
|
.size = size,
|
||||||
.block_sizes = sizes,
|
.block_sizes = sizes,
|
||||||
};
|
};
|
||||||
@@ -40,7 +40,7 @@ pub const ExtFile = struct {
|
|||||||
sparse: u64,
|
sparse: u64,
|
||||||
hard_links: u32,
|
hard_links: u32,
|
||||||
frag_idx: u32,
|
frag_idx: u32,
|
||||||
block_offset: u32,
|
frag_offset: u32,
|
||||||
xattr_idx: u32,
|
xattr_idx: u32,
|
||||||
block_sizes: []BlockSize,
|
block_sizes: []BlockSize,
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ pub const ExtFile = struct {
|
|||||||
.sparse = std.mem.readVarInt(u64, buf[16..24], .little),
|
.sparse = std.mem.readVarInt(u64, buf[16..24], .little),
|
||||||
.hard_links = std.mem.readVarInt(u32, buf[24..28], .little),
|
.hard_links = std.mem.readVarInt(u32, buf[24..28], .little),
|
||||||
.frag_idx = frag_idx,
|
.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),
|
.xattr_idx = std.mem.readVarInt(u32, buf[36..40], .little),
|
||||||
.block_sizes = sizes,
|
.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