Further progress in parsing format
This commit is contained in:
+131
-2
@@ -1,3 +1,132 @@
|
|||||||
const import = @import("std");
|
const std = @import("std");
|
||||||
|
const config = @import("config");
|
||||||
|
const squashfs = @import("squashfs");
|
||||||
|
|
||||||
pub fn main() void {}
|
const help_msg =
|
||||||
|
\\Basic Usage: zig-unsquashfs [Options] SQUASHFS_FILE <EXTRACT_LOCATION>
|
||||||
|
\\
|
||||||
|
\\General options:
|
||||||
|
\\ -e <path> Path to a file or directory inside the archive to extract instead of the whole archive.
|
||||||
|
\\ Can be given multiple times.
|
||||||
|
\\ -o <bytes> Skip <bytes> before reading from the archive.
|
||||||
|
\\ -v Verbose output.
|
||||||
|
\\
|
||||||
|
\\Extraction options:
|
||||||
|
\\ --unbreak-symlinks Attempt extract symlink targets along with symlinks. Will not place files outside of the extraction location.
|
||||||
|
\\ -us Same as --unbreak-symlinks
|
||||||
|
\\ --deref-symlinks Replace symlink files with their target.
|
||||||
|
\\ -ds Same as --deref-symlinks
|
||||||
|
\\ -p <#> Use at most # of processors. Defaults to logical core count.
|
||||||
|
\\
|
||||||
|
\\Listing Options:
|
||||||
|
\\ -l List files instead of extracting. When used, you do not need to specify an extraction location.
|
||||||
|
\\ -ll Similiar to -l, but with file attributes.
|
||||||
|
\\ -lln Similiar to -ll, but with numeric uids and gids.
|
||||||
|
\\
|
||||||
|
\\Other:
|
||||||
|
\\ --help Prints this help message.
|
||||||
|
\\ -h Same as --help
|
||||||
|
\\ --version Print version number.
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
|
||||||
|
const stdout = std.io.getStdOut();
|
||||||
|
|
||||||
|
var extr_files: std.ArrayList([]const u8) = undefined;
|
||||||
|
var offset: u64 = 0;
|
||||||
|
var verbose: bool = false;
|
||||||
|
var unbreak: bool = false;
|
||||||
|
var deref: bool = false;
|
||||||
|
var processors: u16 = 0;
|
||||||
|
var list: ListTypes = .None;
|
||||||
|
|
||||||
|
var filename: []const u8 = "";
|
||||||
|
var extr_location: []const u8 = "";
|
||||||
|
|
||||||
|
const ListTypes = enum {
|
||||||
|
None,
|
||||||
|
List,
|
||||||
|
ListAttr,
|
||||||
|
ListNumeric,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var alloc: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||||
|
extr_files = .init(alloc.allocator());
|
||||||
|
defer extr_files.deinit();
|
||||||
|
var args = std.process.argsWithAllocator(alloc.allocator()) catch {
|
||||||
|
_ = try stdout.writeAll("Unable to allocate memory");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer args.deinit();
|
||||||
|
_ = args.next();
|
||||||
|
while (args.next()) |arg| {
|
||||||
|
if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
|
||||||
|
_ = try stdout.writeAll(help_msg);
|
||||||
|
return;
|
||||||
|
} else if (std.mem.eql(u8, arg, "--version")) {
|
||||||
|
try config.version.format("", .{}, stdout.writer());
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
return;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-v")) {
|
||||||
|
verbose = true;
|
||||||
|
} else if (std.mem.eql(u8, arg, "--unbreak-symlinks") or std.mem.eql(u8, arg, "-us")) {
|
||||||
|
unbreak = true;
|
||||||
|
} else if (std.mem.eql(u8, arg, "--deref-symlinks") or std.mem.eql(u8, arg, "-ds")) {
|
||||||
|
deref = true;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-l")) {
|
||||||
|
list = .List;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-ll")) {
|
||||||
|
list = .ListAttr;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-lln")) {
|
||||||
|
list = .ListNumeric;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-e")) {
|
||||||
|
const next = args.next();
|
||||||
|
if (next == null) {
|
||||||
|
_ = try stdout.writeAll("path required after -e\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try extr_files.append(next.?);
|
||||||
|
} else if (std.mem.eql(u8, arg, "-o")) {
|
||||||
|
const next = args.next();
|
||||||
|
if (next == null) {
|
||||||
|
_ = try stdout.writeAll("offset required after -o\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offset = try std.fmt.parseInt(u64, next.?, 10);
|
||||||
|
} else if (std.mem.eql(u8, arg, "-p")) {
|
||||||
|
const next = args.next();
|
||||||
|
if (next == null) {
|
||||||
|
_ = try stdout.writeAll("number required after -p\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processors = try std.fmt.parseInt(u16, next.?, 10);
|
||||||
|
} else if (filename.len == 0) {
|
||||||
|
filename = arg;
|
||||||
|
} else if (extr_location.len == 0) {
|
||||||
|
extr_location = arg;
|
||||||
|
} else {
|
||||||
|
_ = try stdout.writeAll("invalid or too many arguments\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filename.len == 0) {
|
||||||
|
_ = try stdout.writeAll("no archive given\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (list == .None and extr_location.len == 0) {
|
||||||
|
_ = try stdout.writeAll("no extract location given\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fil = try std.fs.cwd().openFile(filename, .{});
|
||||||
|
var rdr = squashfs.FileReader.init(
|
||||||
|
alloc.allocator(),
|
||||||
|
fil,
|
||||||
|
offset,
|
||||||
|
) catch |err| {
|
||||||
|
try std.fmt.format(stdout.writer(), "Error opening {s} as squashfs: {any}\n", .{ filename, err });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer rdr.deinit();
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const InodeType = @import("inode.zig").Type;
|
||||||
|
const Compression = @import("superblock.zig").Compression;
|
||||||
|
|
||||||
|
const Header = extern struct { //use extern instead of packed, due to bit alignment
|
||||||
|
count: u32,
|
||||||
|
block: u32,
|
||||||
|
num: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RawEntry = struct {
|
||||||
|
offset: u16,
|
||||||
|
num_offset: i16,
|
||||||
|
type: InodeType,
|
||||||
|
size: u16,
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator, rdr: anytype) !RawEntry {
|
||||||
|
const fixed: [8]u8 = undefined;
|
||||||
|
_ = try rdr.read(&fixed);
|
||||||
|
const size = std.mem.readInt(u16, fixed[6..8], .little);
|
||||||
|
const name = try alloc.alloc(u8, size + 1);
|
||||||
|
_ = try rdr.read(name);
|
||||||
|
return .{
|
||||||
|
.offset = std.mem.readInt(u16, fixed[0..2], .little),
|
||||||
|
.num_offset = std.mem.readInt(i16, fixed[2..4], .little),
|
||||||
|
.type = std.mem.readInt(u16, fixed[4..6], .little),
|
||||||
|
.size = size,
|
||||||
|
.name = name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Entry = struct {
|
||||||
|
block: u32,
|
||||||
|
offset: u16,
|
||||||
|
num: u32,
|
||||||
|
type: InodeType,
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
|
pub fn deinit(self: Entry, alloc: std.mem.Allocator) void {
|
||||||
|
alloc.free(self.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn readDirectory(alloc: std.mem.Allocator, rdr: anytype, size: u32) []Entry {
|
||||||
|
const entries: std.ArrayList(Entry) = .init(alloc);
|
||||||
|
errdefer entries.deinit();
|
||||||
|
var cur_red: u32 = 3; // dir size includes "." & "..", so its actual size is off by 3.
|
||||||
|
var hdr: Header = undefined;
|
||||||
|
while (cur_red < size) {
|
||||||
|
_ = try rdr.read(std.mem.asBytes(&hdr));
|
||||||
|
cur_red += 12;
|
||||||
|
try entries.ensureUnusedCapacity(hdr.count + 1);
|
||||||
|
for (0..hdr.count + 1) |_| {
|
||||||
|
const raw_ent: RawEntry = try .init(alloc, rdr);
|
||||||
|
cur_red += 9 + raw_ent.size;
|
||||||
|
errdefer alloc.free(raw_ent.name);
|
||||||
|
entries.appendAssumeCapacity(.{
|
||||||
|
.block = hdr.block,
|
||||||
|
.offset = raw_ent.offset,
|
||||||
|
.num = hdr.num + raw_ent.num_offset,
|
||||||
|
.type = raw_ent.type,
|
||||||
|
.name = raw_ent.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries.toOwnedSlice();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const BlockSize = @import("inode/file.zig").BlockSize;
|
||||||
|
|
||||||
|
pub const FragEntry = packed struct {
|
||||||
|
block: u64,
|
||||||
|
size: BlockSize,
|
||||||
|
_: u32,
|
||||||
|
};
|
||||||
+14
-4
@@ -4,13 +4,17 @@ 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 ToRead = @import("reader/to_read.zig").ToRead;
|
||||||
|
const Compression = @import("superblock.zig").Compression;
|
||||||
|
const MetadataReader = @import("reader/metadata.zig").MetadataReader;
|
||||||
|
|
||||||
pub const Ref = packed struct {
|
pub const Ref = packed struct {
|
||||||
offset: u16,
|
offset: u16,
|
||||||
block: u32,
|
block: u32,
|
||||||
_: u16,
|
_: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Type = enum(u16) {
|
pub const Type = enum(u16) {
|
||||||
dir = 1,
|
dir = 1,
|
||||||
file,
|
file,
|
||||||
symlink,
|
symlink,
|
||||||
@@ -27,7 +31,7 @@ const Type = enum(u16) {
|
|||||||
ext_socket,
|
ext_socket,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = packed struct {
|
pub const Header = packed struct {
|
||||||
type: Type,
|
type: Type,
|
||||||
perm: u16,
|
perm: u16,
|
||||||
uid_idx: u16,
|
uid_idx: u16,
|
||||||
@@ -36,7 +40,7 @@ const Header = packed struct {
|
|||||||
num: u32,
|
num: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Data = union(enum) {
|
pub const Data = union(enum) {
|
||||||
dir: dir.Dir,
|
dir: dir.Dir,
|
||||||
file: file.File,
|
file: file.File,
|
||||||
symlink: misc.Symlink,
|
symlink: misc.Symlink,
|
||||||
@@ -59,7 +63,6 @@ hdr: Header,
|
|||||||
data: Data,
|
data: Data,
|
||||||
|
|
||||||
pub fn init(rdr: anytype, alloc: std.mem.Allocator, block_size: u32) !Self {
|
pub fn init(rdr: anytype, alloc: std.mem.Allocator, block_size: u32) !Self {
|
||||||
std.debug.assert(std.meta.hasFn(@TypeOf(rdr), "read"));
|
|
||||||
var hdr: Header = undefined;
|
var hdr: Header = undefined;
|
||||||
_ = try rdr.read(std.mem.asBytes(&hdr));
|
_ = try rdr.read(std.mem.asBytes(&hdr));
|
||||||
const data = switch (hdr.type) {
|
const data = switch (hdr.type) {
|
||||||
@@ -83,3 +86,10 @@ pub fn init(rdr: anytype, alloc: std.mem.Allocator, block_size: u32) !Self {
|
|||||||
.data = data,
|
.data = data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub fn initFromRef(p_rdr: anytype, comp: Compression, ref: Ref, table_start: u64, alloc: std.mem.Allocator, block_size: u32) !Self {
|
||||||
|
const rdr: ToRead(@TypeOf(p_rdr)) = .init(p_rdr, ref.block + table_start);
|
||||||
|
const meta_rdr: MetadataReader(ToRead(@TypeOf(p_rdr))) = try .init(alloc, comp, rdr);
|
||||||
|
defer meta_rdr.deinit();
|
||||||
|
try meta_rdr.skip(ref.offset);
|
||||||
|
return init(meta_rdr, alloc, block_size);
|
||||||
|
}
|
||||||
|
|||||||
+33
-5
@@ -1,25 +1,53 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Inode = @import("inode.zig");
|
||||||
|
const Table = @import("table.zig").Table;
|
||||||
|
const PRead = @import("reader/p_read.zig").PRead;
|
||||||
|
const FragEntry = @import("fragment.zig").FragEntry;
|
||||||
const Superblock = @import("superblock.zig").Superblock;
|
const Superblock = @import("superblock.zig").Superblock;
|
||||||
|
|
||||||
pub fn Reader(comptime T: type) type {
|
pub fn Reader(comptime T: type) type {
|
||||||
std.debug.assert(std.meta.hasFn(T, "pread"));
|
comptime std.debug.assert(std.meta.hasFn(T, "pread"));
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
rdr: T,
|
rdr: PRead(T),
|
||||||
|
|
||||||
super: Superblock = undefined,
|
super: Superblock = undefined,
|
||||||
|
/// ID table. Can be accessed directly
|
||||||
|
id_table: Table(u32, T) = undefined,
|
||||||
|
/// Fragment table. Can be accessed directly
|
||||||
|
frag_table: Table(FragEntry, T) = undefined,
|
||||||
|
/// Export table. Each element is an inode referce.
|
||||||
|
/// If accessing directly, keep in mind, the table starts at inode 1, as such it's recommended to use the InodeAt function instead.
|
||||||
|
export_table: Table(Inode.Ref, T) = undefined,
|
||||||
|
root: ?Inode = null,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, rdr: T) Self {
|
pub fn init(alloc: std.mem.Allocator, rdr: T, offset: u64) !Self {
|
||||||
const out = Self{
|
var out: Self = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.rdr = rdr,
|
.rdr = .init(rdr, offset),
|
||||||
};
|
};
|
||||||
_ = try rdr.pread(std.mem.asBytes(&out.super), 0);
|
_ = try rdr.pread(std.mem.asBytes(&out.super), 0);
|
||||||
|
out.frag_table = .init(alloc, rdr, out.super.frag_start, out.super.frag_count);
|
||||||
|
out.id_table = .init(alloc, rdr, out.super.id_start, out.super.id_count);
|
||||||
|
out.export_table = .init(alloc, rdr, out.super.export_start, out.super.inode_count);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.id_table.deinit();
|
||||||
|
self.frag_table.deinit();
|
||||||
|
self.export_table.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the inode with the given Inode Number.
|
||||||
|
/// Requires for the archive to have an export table.
|
||||||
|
pub fn InodeAt(self: Self, num: u32) !Inode {
|
||||||
|
const ref = try self.export_table.get(num - 1);
|
||||||
|
_ = ref;
|
||||||
|
return error{TODO}.TODO;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const MetaHeader = packed struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn MetadataReader(comptime T: type) type {
|
pub fn MetadataReader(comptime T: type) type {
|
||||||
|
comptime std.debug.assert(std.meta.hasFn(T, "read"));
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
@@ -38,6 +39,31 @@ pub fn MetadataReader(comptime T: type) type {
|
|||||||
std.io.limitedReader(self.rdr, hdr.size),
|
std.io.limitedReader(self.rdr, hdr.size),
|
||||||
self.block,
|
self.block,
|
||||||
);
|
);
|
||||||
|
self.block_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip(self: *Self, offset: u32) !void {
|
||||||
|
var skipped = 0;
|
||||||
|
var to_skip = 0;
|
||||||
|
while (skipped < offset) {
|
||||||
|
if (self.block_offset >= self.block_size) try self.readNextBlock();
|
||||||
|
to_skip = @min(self.block_size - self.block_offset, offset - skipped);
|
||||||
|
self.block_offset += to_skip;
|
||||||
|
skipped += to_skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(self: *Self, buf: []u8) !usize {
|
||||||
|
var cur_red: usize = 0;
|
||||||
|
var to_read: usize = 0;
|
||||||
|
while (cur_red < buf.len) {
|
||||||
|
if (self.block_offset >= self.block_size) try self.readNextBlock();
|
||||||
|
to_read = @min(buf.len - cur_red, self.block_size - self.block_offset);
|
||||||
|
@memcpy(buf[cur_red .. cur_red + to_read], self.block[self.block_offset .. self.block_offset + to_read]);
|
||||||
|
cur_red += to_read;
|
||||||
|
self.block_offset += to_read;
|
||||||
|
}
|
||||||
|
return cur_red;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ToRead = @import("to_read.zig").ToRead;
|
||||||
|
|
||||||
|
/// A simple wrapper around a type with the pread([]u8, u64) function.
|
||||||
|
/// Provides a couple useful utility functions.
|
||||||
|
pub fn PRead(comptime T: type) type {
|
||||||
|
comptime std.debug.assert(std.meta.hasFn(T, "pread"));
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
rdr: T,
|
||||||
|
offset: u64,
|
||||||
|
|
||||||
|
pub fn init(rdr: T, offset: u64) Self {
|
||||||
|
return .{
|
||||||
|
.rdr = rdr,
|
||||||
|
.offset = offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pread(self: Self, buf: []u8, offset: u64) !usize {
|
||||||
|
return self.rdr.pread(buf, self.offset + offset);
|
||||||
|
}
|
||||||
|
pub fn readerAt(self: Self, offset: u64) ToRead(T) {
|
||||||
|
return .init(self.rdr, self.offset + offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn ToRead(comptime T: type) type {
|
||||||
|
comptime std.debug.assert(std.meta.hasFn(T, "pread"));
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
rdr: T,
|
||||||
|
offset: u64,
|
||||||
|
|
||||||
|
pub fn init(rdr: T, init_offset: u64) Self {
|
||||||
|
return .{
|
||||||
|
.rdr = rdr,
|
||||||
|
.offset = init_offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(self: *Self, buf: []u8) !usize {
|
||||||
|
const red = try self.rdr.pread(buf, self.offset);
|
||||||
|
self.offset += red;
|
||||||
|
return red;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Reader = @import("reader.zig").Reader;
|
||||||
|
|
||||||
|
pub const FileReader = Reader(std.fs.File);
|
||||||
|
|||||||
+5
-1
@@ -1,5 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const InodeRef = @import("inode.zig").Ref;
|
||||||
|
|
||||||
pub const Superblock = packed struct {
|
pub const Superblock = packed struct {
|
||||||
magic: u32,
|
magic: u32,
|
||||||
inode_count: u32,
|
inode_count: u32,
|
||||||
@@ -26,7 +28,7 @@ pub const Superblock = packed struct {
|
|||||||
id_count: u16,
|
id_count: u16,
|
||||||
ver_maj: u16,
|
ver_maj: u16,
|
||||||
ver_min: u16,
|
ver_min: u16,
|
||||||
root_ref: u64,
|
root_ref: InodeRef,
|
||||||
size: u64,
|
size: u64,
|
||||||
id_start: u64,
|
id_start: u64,
|
||||||
xattr_start: u64,
|
xattr_start: u64,
|
||||||
@@ -57,11 +59,13 @@ pub const Compression = enum(u16) {
|
|||||||
},
|
},
|
||||||
.lzma => {
|
.lzma => {
|
||||||
const decomp = try std.compress.lzma.decompress(alloc, source);
|
const decomp = try std.compress.lzma.decompress(alloc, source);
|
||||||
|
defer decomp.deinit();
|
||||||
return decomp.read(dest);
|
return decomp.read(dest);
|
||||||
},
|
},
|
||||||
.lzo => return DecompressError.LzoUnavailable,
|
.lzo => return DecompressError.LzoUnavailable,
|
||||||
.xz => {
|
.xz => {
|
||||||
const decomp = try std.compress.xz.decompress(alloc, source);
|
const decomp = try std.compress.xz.decompress(alloc, source);
|
||||||
|
defer decomp.deinit();
|
||||||
return decomp.read(dest);
|
return decomp.read(dest);
|
||||||
},
|
},
|
||||||
.lz4 => return DecompressError.Lz4Unavailable,
|
.lz4 => return DecompressError.Lz4Unavailable,
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const TableError = error{
|
||||||
|
InvalidIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn Table(comptime T: type, comptime R: type) type {
|
||||||
|
comptime std.debug.assert(std.meta.hasFn(R, "pread"));
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
rdr: R,
|
||||||
|
|
||||||
|
offset: u64,
|
||||||
|
table_count: u32,
|
||||||
|
mut: std.Thread.RwLock = .{},
|
||||||
|
|
||||||
|
table: []T = &[0]T{},
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator, rdr: R, offset: u64, table_count: u32) Self {
|
||||||
|
return .{
|
||||||
|
.alloc = alloc,
|
||||||
|
.rdr = rdr,
|
||||||
|
.offset = offset,
|
||||||
|
.table_count = table_count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn deinit(self: Self) void {
|
||||||
|
self.alloc.free(self.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(self: *Self, to_add: usize) !void {
|
||||||
|
if (!self.alloc.resize(self.table, self.table.len + to_add)) {
|
||||||
|
const new_table = try self.alloc.alloc(T, self.table.len + to_add);
|
||||||
|
@memcpy(new_table[0..self.table.len], self.table);
|
||||||
|
self.alloc.free(self.table);
|
||||||
|
self.table = new_table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *Self, idx: u32) !T {
|
||||||
|
if (idx >= self.table_count) return TableError.InvalidIndex;
|
||||||
|
self.mut.lockShared();
|
||||||
|
defer self.mut.unlockShared();
|
||||||
|
if (idx >= self.table.len) {
|
||||||
|
return self.getAndFill(idx);
|
||||||
|
}
|
||||||
|
return self.table[idx];
|
||||||
|
}
|
||||||
|
fn getAndFill(self: *Self, idx: u32) !T {
|
||||||
|
self.mut.unlockShared();
|
||||||
|
defer self.mut.lockShared();
|
||||||
|
self.mut.lock();
|
||||||
|
defer self.mut.unlock();
|
||||||
|
var to_read: usize = 0;
|
||||||
|
var offset: u64 = 0;
|
||||||
|
while (idx >= self.table.len) {
|
||||||
|
to_read = @min(self.table_count - self.table.len, comptime 8192 / @sizeOf(T));
|
||||||
|
try self.resize(to_read);
|
||||||
|
_ = try self.rdr.pread(std.mem.asBytes(&offset), self.offset);
|
||||||
|
self.offset += 8;
|
||||||
|
_ = try self.rdr.pread(std.mem.sliceAsBytes(self.table[self.table.len - to_read ..]), offset);
|
||||||
|
}
|
||||||
|
return self.table[idx];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user